/*! * @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)); } }); } }; }));