/*!
* @preserve
*
* readmore.js jquery plugin
* author: @jed_foster
* project home: http://jedfoster.github.io/readmore.js
* licensed under the mit license
*
* debounce function from http://davidwalsh.name/javascript-debounce-function
*/
/* global jquery */
//在$(function)里面的都是一次性对象发函数
(function(factory) {
if (typeof define === 'function' && define.amd) {
// amd
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// commonjs
module.exports = factory(require('jquery'));
} else {
// browser globals
factory(jquery);
}
}(function($) {
'use strict';
var readmore = 'readmore',
defaults = {
speed: 100,
collapsedheight: 200,
heightmargin: 16,
morelink: 'read more',
lesslink: 'close',
embedcss: true,
blockcss: 'display: block; width: 100%;',
startopen: false,
// callbacks
blockprocessed: function() {},
beforetoggle: function() {},
aftertoggle: function() {}
},
cssembedded = {},
uniqueidcounter = 0;
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (! immediate) {
func.apply(context, args);
}
};
var callnow = immediate && !timeout;
cleartimeout(timeout);
timeout = settimeout(later, wait);
if (callnow) {
func.apply(context, args);
}
};
}
function uniqueid(prefix) {
var id = ++uniqueidcounter;
return string(prefix == null ? 'rmjs-' : prefix) + id;
}
function setboxheights(element) {
var el = element.clone().css({
height: 'auto',
width: element.width(),
maxheight: 'none',
overflow: 'hidden'
}).insertafter(element),
expandedheight = el.outerheight(true),
cssmaxheight = parseint(el.css({maxheight: ''}).css('max-height').replace(/[^-\d\.]/g, ''), 10),
defaultheight = element.data('defaultheight');
el.remove();
var collapsedheight = cssmaxheight || element.data('collapsedheight') || defaultheight;
if (element.data('expandedheight') != undefined && element.data('expandedheight') != '' && element.data('expandedheight') != null) {
expandedheight = number(expandedheight) > number(element.data('expandedheight')) ? expandedheight : element.data('expandedheight');
}
// store our measurements.
element.data({
expandedheight: expandedheight,
maxheight: cssmaxheight,
collapsedheight: collapsedheight
})
// and disable any `max-height` property set in css
.css({
maxheight: 'none'
});
}
var resizeboxes = debounce(function() {
$('[data-readmore]').each(function() {
var current = $(this),
isexpanded = (current.attr('aria-expanded') === 'true');
setboxheights(current);
current.css({
height: current.data( (isexpanded ? 'expandedheight' : 'collapsedheight') )
});
});
}, 100);
function embedcss(options) {
if (! cssembedded[options.selector]) {
var styles = ' ';
if (options.embedcss && options.blockcss !== '') {
styles += options.selector + ' + [data-readmore-toggle], ' +
options.selector + '[data-readmore]{' +
options.blockcss +
'}';
}
// include the transition css even if embedcss is false
styles += options.selector + '[data-readmore]{' +
'transition: height ' + options.speed + 'ms;' +
'overflow: hidden;' +
'}';
(function(d, u) {
var css = d.createelement('style');
css.type = 'text/css';
if (css.stylesheet) {
css.stylesheet.csstext = u;
}
else {
css.appendchild(d.createtextnode(u));
}
d.getelementsbytagname('head')[0].appendchild(css);
}(document, styles));
cssembedded[options.selector] = true;
}
}
function readmore(element, options) {
this.element = element;
this.options = $.extend({}, defaults, options);
embedcss(this.options);
this._defaults = defaults;
this._name = readmore;
this.init();
// ie8 chokes on `window.addeventlistener`, so need to test for support.
if (window.addeventlistener) {
// need to resize boxes when the page has fully loaded.
window.addeventlistener('load', resizeboxes);
window.addeventlistener('resize', resizeboxes);
}
else {
window.attachevent('load', resizeboxes);
window.attachevent('resize', resizeboxes);
}
}
readmore.prototype = {
init: function() {
var current = $(this.element);
current.data({
defaultheight: this.options.collapsedheight,
heightmargin: this.options.heightmargin
});
setboxheights(current);
var collapsedheight = current.data('collapsedheight'),
heightmargin = current.data('heightmargin');
if (current.outerheight(true) <= collapsedheight + heightmargin) {
// the block is shorter than the limit, so there's no need to truncate it.
if (this.options.blockprocessed && typeof this.options.blockprocessed === 'function') {
this.options.blockprocessed(current, false);
}
return true;
}
else {
var id = current.attr('id') || uniqueid(),
uselink = this.options.startopen ? this.options.lesslink : this.options.morelink;
current.attr({
'data-readmore': '',
'aria-expanded': this.options.startopen,
'id': id
});
current.after($(uselink)
.on('click', (function(_this) {
return function(event) {
_this.toggle(this, current[0], event);
};
})(this))
.attr({
'data-readmore-toggle': id,
'aria-controls': id
}));
if (! this.options.startopen) {
current.css({
height: collapsedheight
});
}
if (this.options.blockprocessed && typeof this.options.blockprocessed === 'function') {
this.options.blockprocessed(current, true);
}
}
},
toggle: function(trigger, element, event) {
if (event) {
event.preventdefault();
}
if (! trigger) {
trigger = $('[aria-controls="' + this.element.id + '"]')[0];
}
if (! element) {
element = this.element;
}
var $element = $(element),
newheight = '',
newlink = '',
expanded = false,
collapsedheight = $element.data('collapsedheight');
if ($element.height() <= collapsedheight) {
newheight = $element.data('expandedheight') + 'px';
newlink = 'lesslink';
expanded = true;
}
else {
newheight = collapsedheight;
newlink = 'morelink';
}
// fire beforetoggle callback
// since we determined the new "expanded" state above we're now out of sync
// with our true current state, so we need to flip the value of `expanded`
if (this.options.beforetoggle && typeof this.options.beforetoggle === 'function') {
this.options.beforetoggle(trigger, $element, ! expanded);
}
// debugger
// if(window.innerwidth<768){
// number(newheight.replace('px','')-300)+'px'
// };
$element.css({'height': newheight});
// fire aftertoggle callback
$element.on('transitionend', (function(_this) {
return function() {
if (_this.options.aftertoggle && typeof _this.options.aftertoggle === 'function') {
_this.options.aftertoggle(trigger, $element, expanded);
}
$(this).attr({
'aria-expanded': expanded
}).off('transitionend');
}
})(this));
$(trigger).replacewith($(this.options[newlink])
.on('click', (function(_this) {
return function(event) {
_this.toggle(this, element, event);
};
})(this))
.attr({
'data-readmore-toggle': $element.attr('id'),
'aria-controls': $element.attr('id')
}));
},
destroy: function() {
$(this.element).each(function() {
var current = $(this);
current.attr({
'data-readmore': null,
'aria-expanded': null
})
.css({
maxheight: '',
height: ''
})
.next('[data-readmore-toggle]')
.remove();
current.removedata();
});
}
};
$.fn.readmore = function(options) {
var args = arguments,
selector = this.selector;
options = options || {};
if (typeof options === 'object') {
return this.each(function() {
if ($.data(this, 'plugin_' + readmore)) {
var instance = $.data(this, 'plugin_' + readmore);
instance.destroy.apply(instance);
}
options.selector = selector;
$.data(this, 'plugin_' + readmore, new readmore(this, options));
});
}
else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
return this.each(function () {
var instance = $.data(this, 'plugin_' + readmore);
if (instance instanceof readmore && typeof instance[options] === 'function') {
instance[options].apply(instance, array.prototype.slice.call(args, 1));
}
});
}
};
}));