summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--CHANGELOG.md36
-rw-r--r--README.md157
-rw-r--r--bower.json2
-rw-r--r--demo.html164
-rw-r--r--gulpfile.js14
-rw-r--r--package.json31
-rw-r--r--readmore.js306
-rw-r--r--readmore.min.js18
9 files changed, 534 insertions, 198 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7295724
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+.*
+node_modules
+bower_components
+coverage
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..337dd77
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,36 @@
+# 2.0.0
+
+## New features
+
+- Install with Bower: `bower install readmore`
+- Blocks can now be toggled programmatically: `$('article:nth-of-type(3)').readmore('toggle')`
+- ARIA semantics describe expanded state and relationship between blocks and their toggles
+- Blocks are now assigned an ID if they don't already have one
+- Install development dependencies with NPM
+- Gulp task to minifiy with UglifyJS
+
+## Improvements
+
+- Height calculations on window resize are "debounced", resulting in more efficient rendering
+- Height calculation in general has been improved
+- The value of the `expanded` argument passed to the `beforeToggle` callback now correctly reflects the _pre-toggle_ state
+- Multiple instances are now fully supported: e.g. `$('article').readmore({speed: 200})` and `$('fieldset').readmore({speed: 900})` will work on the same page
+- Fully responsive, plugin now prefers max-heights set in CSS, even inside media queries
+
+## Potentially breaking changes
+
+- `maxHeight` option is now `collapsedHeight`
+- `sectionCSS` option is now `blockCSS`
+- `toggleSlider()` method is now just `toggle()`
+- Animation is now performed with CSS3 transitions, rather than `jQuery.animate()`
+- IE 8 and 9 are no longer supported, because those browsers hate kittens
+- `init()` is now called within a `window.onload` event handler, which can briefly delay collapsing content
+- `setBoxHeight()` is now a "private" method called `setBoxHeights()`
+- `resizeBoxes()` is also now private
+- Readmore.js now uses attribute selectors, rather than classes
+ - The `.readmore-js-section` and `.readmore-js-toggle` classes are gone
+ - The `expandedClass` and `collapsedClass` options are also gone
+ - Every Readmore.js block needs an ID, if one is not already present, one will be generated
+
+
+
diff --git a/README.md b/README.md
index 133c83d..0116fbe 100644
--- a/README.md
+++ b/README.md
@@ -1,43 +1,60 @@
-Readmore.js
-========
+# Readmore.js
-A smooth, lightweight jQuery plugin for collapsing and expanding long blocks of text with "Read more" and "Close" links.
+A smooth, responsive jQuery plugin for collapsing and expanding long blocks of text with "Read more" and "Close" links.
-The required markup for Readmore.js is also extremely lightweight and very simple. No need for complicated sets of `div`s or hardcoded class names, just call ``.readmore()`` on the element containing your block of text and Readmore.js takes care of the rest.
+The markup Readmore.js requires is so simple, you can probably use it with your existing HTML—there's no need for complicated sets of `div`'s or hardcoded classes, just call `.readmore()` on the element containing your block of text and Readmore.js takes care of the rest. Readmore.js plays well in a responsive environment, too.
-Readmore.js is compatible with all versions of jQuery greater than 1.7.0.
+Readmore.js is tested with—and supported on—all versions of jQuery greater than 1.9.1. All the "good" browsers are supported, as well as IE10+; IE8 & 9 _should_ work, but are not supported and the experience will not be ideal.
-## Example ##
- $('article').readmore();
-
-Yes, it's that simple. You can change the speed of the animation, the height of the collapsed block, and the open and close elements.
+## Install
- $('article').readmore({
- speed: 75,
- maxHeight: 500
- });
+Install Readmore.js with Bower:
-## The options: ##
+```
+$ bower install readmore
+```
+
+Then include it in your HTML:
+
+```html
+<script src="/bower_components/readmore/readmore.min.js"></script>
+```
+
+
+## Use
+
+```javascript
+$('article').readmore();
+```
+
+It's that simple. You can change the speed of the animation, the height of the collapsed block, and the open and close elements.
+
+```javascript
+$('article').readmore({
+ speed: 75,
+ lessLink: '<a href="#">Read less</a>'
+});
+```
-* `speed: 100` (in milliseconds)
-* `maxHeight: 200` (in pixels)
-* `heightMargin: 16` (in pixels, avoids collapsing blocks that are only slightly larger than `maxHeight`)
+### The options:
+
+* `speed: 100` in milliseconds
+* `collapsedHeight: 200` in pixels
+* `heightMargin: 16` in pixels, avoids collapsing blocks that are only slightly larger than `collapsedHeight`
* `moreLink: '<a href="#">Read more</a>'`
* `lessLink: '<a href="#">Close</a>'`
-* `embedCSS: true` (insert required CSS dynamically, set this to `false` if you include the necessary CSS in a stylesheet)
-* `sectionCSS: 'display: block; width: 100%;'` (sets the styling of the blocks, ignored if `embedCSS` is `false`)
-* `startOpen: false` (do not immediately truncate, start in the fully opened position)
-* `expandedClass: 'readmore-js-expanded'` (class added to expanded blocks)
-* `collapsedClass: 'readmore-js-collapsed'` (class added to collapsed blocks)
-* `beforeToggle: function() {}` (called after a more or less link is clicked, but *before* the block is collapsed or expanded)
-* `afterToggle: function() {}` (called *after* the block is collapsed or expanded)
+* `embedCSS: true` insert required CSS dynamically, set this to `false` if you include the necessary CSS in a stylesheet
+* `blockCSS: 'display: block; width: 100%;'` sets the styling of the blocks, ignored if `embedCSS` is `false`
+* `startOpen: false` do not immediately truncate, start in the fully opened position
+* `beforeToggle: function() {}` called after a more or less link is clicked, but *before* the block is collapsed or expanded
+* `afterToggle: function() {}` called *after* the block is collapsed or expanded
-If the element has a `max-height` CSS property, Readmore.js will use that value rather than the value of the `maxHeight` option.
+If the element has a `max-height` CSS property, Readmore.js will use that value rather than the value of the `collapsedHeight` option.
### The callbacks:
-The callback functions, `beforeToggle()` and `afterToggle`, both receive the same arguments: `trigger`, `element`, and `expanded`.
+The callback functions, `beforeToggle` and `afterToggle`, both receive the same arguments: `trigger`, `element`, and `expanded`.
* `trigger`: the "Read more" or "Close" element that was clicked
* `element`: the block that is being collapsed or expanded
@@ -57,44 +74,98 @@ $('article').readmore({
});
```
-### Recommended CSS:
+### Removing Readmore:
+
+You can remove the Readmore.js functionality like so:
+
+```javascript
+$('article').readmore('destroy');
+```
+
+Or, you can be more surgical by specifying a particular element:
+
+```javascript
+$('article:first').readmore('destroy');
+```
+
+### Toggling blocks programmatically:
+
+You can toggle a block from code:
+
+```javascript
+$('article:nth-of-type(3)').readmore('toggle');
+```
+
+
+## CSS:
-The intention behind Readmore.js is to use CSS for as much functionality as possible. In particular, "collapsing" is achieved by setting `overflow: hidden` on the containing block and changing the `height` property.
+Readmore.js is designed to use CSS for as much functionality as possible: collapsed height can be set in CSS with the `max-height` property; "collapsing" is achieved by setting `overflow: hidden` on the containing block and changing the `height` property; and, finally, the expanding/collapsing animation is done with CSS3 transitions.
-By default, Readmore.js inserts the following CSS:
+By default, Readmore.js inserts the following CSS, in addition to some transition-related rules:
```css
-.readmore-js-toggle, .readmore-js-section {
+selector + [data-readmore-toggle], selector[data-readmore] {
display: block;
width: 100%;
}
-.readmore-js-section {
- overflow: hidden;
-}
```
-You can override the the first set of rules when you set up Readmore.js like so:
+_`selector` would be the element you invoked `readmore()` on, e.g.: `$('selector').readmore()`_
+
+You can override the base rules when you set up Readmore.js like so:
```javascript
-$('article').readmore({sectionCSS: 'display: inline-block; width: 50%;'});
+$('article').readmore({blockCSS: 'display: inline-block; width: 50%;'});
```
-If you want to include the necessary styling in your site's stylesheet, you can disable the dynamic embedding by passing `embedCSS: false` in the options hash.
+If you want to include the necessary styling in your site's stylesheet, you can disable the dynamic embedding by setting `embedCSS` to `false`:
```javascript
$('article').readmore({embedCSS: false});
```
-## Removing Readmore
+### Media queries and other CSS tricks:
-You can remove the Readmore functionality like so:
+If you wanted to set a `maxHeight` based on lines, you could do so in CSS with something like:
-```javascript
-$('article').readmore('destroy');
+```css
+body {
+ font: 16px/1.5 sans-serif;
+}
+
+/* Show only 4 lines in smaller screens */
+article {
+ max-height: 6em; /* (4 * 1.5 = 6) */
+}
```
-Or, you can be more surgical by specifying a particular element:
+Then, with a media query you could change the number of lines shown, like so:
-```javascript
-$('article:first').readmore('destroy');
+```css
+/* Show 8 lines on larger screens */
+@media screen and (min-width: 640px) {
+ article {
+ max-height: 12em;
+ }
+}
```
+
+
+## Contributing
+
+Pull requests are always welcome, but not all suggested features will get merged. Feel free to contact me if you have an idea for a feature.
+
+Pull requests should include the minified script and this readme and the demo HTML should be updated with descriptions of your new feature.
+
+You'll need NPM:
+
+```
+$ npm install
+```
+
+Which will install the necessary development dependencies. Then, to build the minified script:
+
+```
+$ gulp compress
+```
+
diff --git a/bower.json b/bower.json
index 31ea545..6cdf55f 100644
--- a/bower.json
+++ b/bower.json
@@ -1,7 +1,7 @@
{
"name": "Readmore.js",
"main": "readmore.min.js",
- "version": "1.0.0",
+ "version": "2.0.0",
"homepage": "http://jedfoster.com/Readmore.js/",
"authors": [
"Jed Foster <jed@jedfoster.com>"
diff --git a/demo.html b/demo.html
index 1a90f11..f09a382 100644
--- a/demo.html
+++ b/demo.html
@@ -31,97 +31,146 @@
<header>
<h1>Readmore.js</h1>
- <p>A smooth, lightweight jQuery plugin for collapsing and expanding long blocks of text with &#8220;Read more&#8221; and &#8220;Close&#8221; links. </p>
+ <p>A smooth, responsive jQuery plugin for collapsing and expanding long blocks of text with &#8220;Read more&#8221; and &#8220;Close&#8221; links.</p>
</header>
<section id="info">
- <p>The required markup for Readmore.js is extremely lightweight. No need for complicated sets of <code>divs</code> or hardcoded class names, just call <code>.readmore()</code> on the element containing your block of text and Readmore.js takes care of the rest. </p>
+ <p>The markup Readmore.js requires is so simple, you can probably use it with your existing HTML—there&#8217;s no need for complicated sets of <code>div</code>&#8217;s or hardcoded classes, just call <code>.readmore()</code> on the element containing your block of text and Readmore.js takes care of the rest. Readmore.js plays well in a responsive environment, too.</p>
- <p>Readmore.js is compatible with all versions of jQuery greater than 1.7.0.</p>
+ <p>Readmore.js is tested with—and supported on—all versions of jQuery greater than 1.9.1. All the &#8220;good&#8221; browsers are supported, as well as IE10+; IE8 &amp; 9 <em>should</em> work, but are not supported and the experience will not be ideal.</p>
- <h2 id="example">Example</h2>
+ <h2 id="install">Install</h2>
- <pre><code class="javascript">$('article').readmore();</code></pre>
+ <p>Install Readmore.js with Bower:</p>
- <p>Yes, it&#8217;s that simple. You can change the speed of the animation, the height of the collapsed block, and the open and close elements.</p>
+ <pre><code>$ bower install readmore</code></pre>
- <pre><code>$('article').readmore({
+ <p>Then include it in your HTML:</p>
+
+ <pre><code class="html">&lt;script src=&quot;/bower_components/readmore/readmore.min.js&quot;&gt;&lt;/script&gt;</code></pre>
+
+ <h2 id="use">Use</h2>
+
+ <pre><code class="javascript">$(&apos;article&apos;).readmore();</code></pre>
+
+ <p>It&#8217;s that simple. You can change the speed of the animation, the height of the collapsed block, and the open and close elements.</p>
+
+ <pre><code class="javascript">$(&apos;article&apos;).readmore({
speed: 75,
- maxHeight: 500
+ lessLink: &apos;&lt;a href=&quot;#&quot;&gt;Read less&lt;/a&gt;&apos;
});</code></pre>
- <h2 id="theoptions">The options:</h2>
+ <h3 id="theoptions">The options:</h3>
<ul>
- <li><code>speed: 100</code> (in milliseconds)</li>
- <li><code>maxHeight: 200</code> (in pixels)</li>
- <li><code>heightMargin: 16</code> (in pixels, avoids collapsing blocks that are only slightly larger than <code>maxHeight</code>)</li>
- <li><code>moreLink: '&lt;a href="#"&gt;Read more&lt;/a&gt;'</code></li>
- <li><code>lessLink: '&lt;a href="#"&gt;Close&lt;/a&gt;'</code></li>
- <li><code>embedCSS: true</code> (insert required CSS dynamically, set this to <code>false</code> if you include the necessary CSS in a stylesheet)</li>
- <li><code>sectionCSS: 'display: block; width: 100%;'</code> (sets the styling of the blocks)</li>
- <li><code>startOpen: false</code> (do not immediately truncate, start in the fully opened position)</li>
- <li><code>expandedClass: 'readmore-js-expanded'</code> (class added to expanded blocks)</li>
- <li><code>collapsedClass: 'readmore-js-collapsed'</code> (class added to collapsed blocks)</li>
- <li><code>beforeToggle: function() {}</code> (called after a more or less link is clicked, but <i>before</i> the block is collapsed or expanded)</li>
- <li><code>afterToggle: function() {}</code> (called <i>after</i> the block is collapsed or expanded)</li>
+ <li><code>speed: 100</code> in milliseconds</li>
+ <li><code>collapsedHeight: 200</code> in pixels</li>
+ <li><code>heightMargin: 16</code> in pixels, avoids collapsing blocks that are only slightly larger than <code>collapsedHeight</code></li>
+ <li><code>moreLink: '&lt;a href=&quot;#&quot;&gt;Read more&lt;/a&gt;'</code></li>
+ <li><code>lessLink: '&lt;a href=&quot;#&quot;&gt;Close&lt;/a&gt;'</code></li>
+ <li><code>embedCSS: true</code> insert required CSS dynamically, set this to <code>false</code> if you include the necessary CSS in a stylesheet</li>
+ <li><code>blockCSS: 'display: block; width: 100%;'</code> sets the styling of the blocks, ignored if <code>embedCSS</code> is <code>false</code></li>
+ <li><code>startOpen: false</code> do not immediately truncate, start in the fully opened position</li>
+ <li><code>beforeToggle: function() {}</code> called after a more or less link is clicked, but <em>before</em> the block is collapsed or expanded</li>
+ <li><code>afterToggle: function() {}</code> called <em>after</em> the block is collapsed or expanded</li>
</ul>
- <p>If the element has a <code>max-height</code> CSS property, Readmore.js will use that value rather than the value of the <code>maxHeight</code> option.</p>
+ <p>If the element has a <code>max-height</code> CSS property, Readmore.js will use that value rather than the value of the <code>collapsedHeight</code> option.</p>
<h3 id="thecallbacks">The callbacks:</h3>
- <p>The callback functions, <code>beforeToggle()</code> and <code>afterToggle</code>, both receive the same arguments: <code>trigger</code>, <code>element</code>, and <code>expanded</code>.</p>
+ <p>The callback functions, <code>beforeToggle</code> and <code>afterToggle</code>, both receive the same arguments: <code>trigger</code>, <code>element</code>, and <code>expanded</code>.</p>
<ul>
- <li><code>trigger</code>: the "Read more" or "Close" element that was clicked</li>
+ <li><code>trigger</code>: the &#8220;Read more&#8221; or &#8220;Close&#8221; element that was clicked</li>
<li><code>element</code>: the block that is being collapsed or expanded</li>
<li><code>expanded</code>: Boolean; <code>true</code> means the block is expanded</li>
</ul>
<h4 id="callbackexample">Callback example:</h4>
- <p>Here's an example of how you could use the <code>afterToggle</code> callback to scroll back to the top of a block when the "Close" link is clicked.</p>
+ <p>Here&#8217;s an example of how you could use the <code>afterToggle</code> callback to scroll back to the top of a block when the &#8220;Close&#8221; link is clicked.</p>
- <pre><code class="javascript">$('article').readmore({
+ <pre><code class="javascript">$(&apos;article&apos;).readmore({
afterToggle: function(trigger, element, expanded) {
- if(! expanded) { // The "Close" link was clicked
- $('html, body').animate( { scrollTop: element.offset().top }, {duration: 100 } );
+ if(! expanded) { // The &quot;Close&quot; link was clicked
+ $(&apos;html, body&apos;).animate( { scrollTop: element.offset().top }, {duration: 100 } );
}
}
});</code></pre>
- <h3 id="recommendedcss">Recommended CSS:</h3>
+ <h3 id="removingreadmore">Removing Readmore:</h3>
+
+ <p>You can remove the Readmore.js functionality like so:</p>
- <p>The intention behind Readmore.js is to use CSS for as much functionality as possible. In particular, &#8220;collapsing&#8221; is achieved by setting <code>overflow: hidden</code> on the containing block and changing the <code>height</code> property.</p>
+ <pre><code class="javascript">$(&apos;article&apos;).readmore(&apos;destroy&apos;);</code></pre>
+
+ <p>Or, you can be more surgical by specifying a particular element:</p>
- <p>By default, Readmore.js inserts the following CSS:</p>
+ <pre><code class="javascript">$(&apos;article:first&apos;).readmore(&apos;destroy&apos;);</code></pre>
- <pre><code class="css">.readmore-js-toggle, .readmore-js-section {
+ <h3 id="togglingblocksprogrammatically">Toggling blocks programmatically:</h3>
+
+ <p>You can toggle a block from code:</p>
+
+ <pre><code class="javascript">$(&apos;article:nth-of-type(3)&apos;).readmore(&apos;toggle&apos;);</code></pre>
+
+ <h2 id="css">CSS:</h2>
+
+ <p>Readmore.js is designed to use CSS for as much functionality as possible: collapsed height can be set in CSS with the <code>max-height</code> property; &#8220;collapsing&#8221; is achieved by setting <code>overflow: hidden</code> on the containing block and changing the <code>height</code> property; and, finally, the expanding/collapsing animation is done with CSS3 transitions.</p>
+
+ <p>By default, Readmore.js inserts the following CSS, in addition to some transition-related rules:</p>
+
+ <pre><code class="css">selector + [data-readmore-toggle], selector[data-readmore] {
display: block;
width: 100%;
-}
-.readmore-js-section {
- overflow: hidden;
}</code></pre>
- <p>You can override the the first set of rules when you set up Readmore.js like so:</p>
+ <p><em><code>selector</code> would be the element you invoked <code>readmore()</code> on, e.g.: <code>$('selector').readmore()</code></em></p>
- <pre><code class="javascript">$(&apos;article&apos;).readmore({sectionCSS: &apos;display: inline-block; width: 50%;&apos;});</code></pre>
+ <p>You can override the base rules when you set up Readmore.js like so:</p>
- <p>If you want to include the necessary styling in your site&#8217;s stylesheet, you can disable the dynamic embedding by passing <code>embedCSS: false</code> in the options hash.</p>
+ <pre><code class="javascript">$(&apos;article&apos;).readmore({blockCSS: &apos;display: inline-block; width: 50%;&apos;});</code></pre>
+
+ <p>If you want to include the necessary styling in your site&#8217;s stylesheet, you can disable the dynamic embedding by setting <code>embedCSS</code> to <code>false</code>:</p>
<pre><code class="javascript">$(&apos;article&apos;).readmore({embedCSS: false});</code></pre>
- <h2 id="removingreadmore">Removing Readmore</h2>
+ <h3 id="mediaqueriesandothercsstricks">Media queries and other CSS tricks:</h3>
- <p>You can remove the Readmore functionality like so:</p>
+ <p>If you wanted to set a <code>maxHeight</code> based on lines, you could do so in CSS with something like:</p>
- <pre><code class="javascript">$('article').readmore('destroy');</code></pre>
+ <pre><code class="css">body {
+ font: 16px/1.5 sans-serif;
+}
- <p>Or, you can be more surgical by specifying a particular element:</p>
+/* Show only 4 lines in smaller screens */
+article {
+ max-height: 6em; /* (4 * 1.5 = 6) */
+}</code></pre>
+
+ <p>Then, with a media query you could change the number of lines shown, like so:</p>
+
+ <pre><code class="css">/* Show 8 lines on larger screens */
+@media screen and (min-width: 640px) {
+ article {
+ max-height: 12em;
+ }
+}</code></pre>
- <pre><code class="javascript">$('article:first').readmore('destroy');</code></pre>
+ <h2 id="contributing">Contributing</h2>
+
+ <p>Pull requests are always welcome, but not all suggested features will get merged. Feel free to contact me if you have an idea for a feature.</p>
+
+ <p>Pull requests should include the minified script and this readme and the demo HTML should be updated with descriptions of your new feature. </p>
+
+ <p>You&#8217;ll need NPM:</p>
+
+ <pre><code>$ npm install</code></pre>
+
+ <p>Which will install the necessary development dependencies. Then, to build the minified script:</p>
+
+ <pre><code>$ gulp compress</code></pre>
</section>
<h1>Demo</h1>
@@ -131,51 +180,52 @@
<article>
<h2>Artisanal Narwahls</h2>
- <p>Salvia portland leggings banh mi fanny pack mixtape, authentic bushwick wes anderson intelligentsia artisan typewriter high life they sold out mixtape high life. Marfa ethnic wayfarers brooklyn keytar mixtape. Blue bottle shoreditch gluten-free, mixtape hoodie whatever pinterest viral twee fashion axe high life irony biodiesel tofu.</p>
+ <p>From this distant vantage point, the Earth might not seem of any particular interest. But for us, it's different. Consider again that dot. That's here. That's home. That's us. On it everyone you love, everyone you know, everyone you ever heard of, every human being who ever was, lived out their lives. The aggregate of our joy and suffering, thousands of confident religions, ideologies, and economic doctrines, every hunter and forager, every hero and coward, every creator and destroyer of civilization, every king and peasant, every young couple in love, every mother and father, hopeful child, inventor and explorer, every teacher of morals, every corrupt politician, every "superstar," every "supreme leader," every saint and sinner in the history of our species lived there – on a mote of dust suspended in a sunbeam.</p>
- <p>Retro church-key thundercats keytar, skateboard irony selvage ethnic freegan banjo pour-over fixie. Raw denim fashion ax eoke locavore disrupt, tonx retro authentic letterpress raw denim stumptown mixtape ugh kale chips flexitarian 90's deep v. Squid fingerstache bespoke wolf DIY. Banjo pour-over shoreditch cardigan try-hard.</p>
+ <p>Space, the final frontier. These are the voyages of the starship Enterprise. Its five year mission: to explore strange new worlds, to seek out new life and new civilizations, to boldly go where no man has gone before!</p>
- <p>Skateboard artisan bicycle rights next level vinyl cardigan beard twee, farm-to-table truffaut. Shoreditch freegan cliche thundercats, bushwick VHS intelligentsia selfies ethnic try-hard before they sold out. Marfa terry richardson hella, seitan odd future pug butcher. Wes anderson tousled YOLO cardigan. Typewriter high life carles, artisan gentrify messenger bag single-origin coffee truffaut thundercats cray 90's pour-over seitan. Banksy 8-bit organic, salvia gentrify stumptown wayfarers. Godard echo park before they sold out chambray, skateboard twee mcsweeney's synth hella.</p>
+ <p>Here's how it is: Earth got used up, so we terraformed a whole new galaxy of Earths, some rich and flush with the new technologies, some not so much. Central Planets, them was formed the Alliance, waged war to bring everyone under their rule; a few idiots tried to fight it, among them myself. I'm Malcolm Reynolds, captain of Serenity. Got a good crew: fighters, pilot, mechanic. We even picked up a preacher, and a bona fide companion. There's a doctor, too, took his genius sister out of some Alliance camp, so they're keeping a low profile. You got a job, we can do it, don't much care what it is.</p>
- <p>Flannel pinterest Austin twee narwhal, hoodie swag wolf photo booth. Hella kale chips marfa occupy pitchfork put a bird on it. Semiotics dreamcatcher selfies beard DIY umami craft beer 3 wolf moon. Try-hard literally mustache polaroid ennui VHS. High life fixie wolf, trust fund twee seitan pinterest blog helvetica sriracha. Tattooed selvage try-hard, biodiesel banjo direct trade echo park kogi tonx bespoke. Pork belly put a bird on it iphone, fixie literally bespoke tonx butcher +1 swag.</p>
+ <p>Space, the final frontier. These are the voyages of the starship Enterprise. Its five year mission: to explore strange new worlds, to seek out new life and new civilizations, to boldly go where no man has gone before!</p>
</article>
<article>
<h2>Portland Leggings</h2>
- <p>Put a bird on it you probably haven't heard of them DIY, vice photo booth terry richardson pickled vegan cray. Pug DIY blog stumptown 3 wolf moon, blue bottle farm-to-table actually banh mi fanny pack quinoa small batch. Mumblecore raw denim hoodie ethnic. Craft beer sriracha mlkshk, organic bicycle rights twee gentrify messenger bag keffiyeh ethical helvetica tumblr. Gluten-free tousled vinyl pop-up. Butcher keytar pickled literally, cosby sweater chambray authentic. Fixie odd future kale chips, church-key pinterest beard pour-over banksy typewriter dreamcatcher squid blue bottle sartorial.</p>
+ <p>Here's how it is: Earth got used up, so we terraformed a whole new galaxy of Earths, some rich and flush with the new technologies, some not so much. Central Planets, them was formed the Alliance, waged war to bring everyone under their rule; a few idiots tried to fight it, among them myself. I'm Malcolm Reynolds, captain of Serenity. Got a good crew: fighters, pilot, mechanic. We even picked up a preacher, and a bona fide companion. There's a doctor, too, took his genius sister out of some Alliance camp, so they're keeping a low profile. You got a job, we can do it, don't much care what it is.</p>
- <p>Banksy blog craft beer PBR fap retro wayfarers polaroid narwhal blue bottle mixtape squid YOLO. Pitchfork brunch put a bird on it, fashion axe squid vegan vice pop-up organic blue bottle forage plaid deep v locavore +1. American apparel 3 wolf moon cray tonx single-origin coffee, gluten-free sartorial. Try-hard iphone pork belly bespoke keffiyeh leggings selfies, chillwave gastropub. Master cleanse plaid hella readymade. Keytar cosby sweater truffaut kogi, try-hard flannel chillwave. Truffaut selvage bespoke banjo freegan, ugh williamsburg.</p>
+ <p>I am Duncan Macleod, born 400 years ago in the Highlands of Scotland. I am Immortal, and I am not alone. For centuries, we have waited for the time of the Gathering when the stroke of a sword and the fall of a head will release the power of the Quickening. In the end, there can be only one.</p>
- <p>Austin single-origin coffee umami vice pork belly, ethical williamsburg flexitarian forage aesthetic street art keytar fashion axe twee. Cliche aesthetic trust fund, williamsburg carles godard four loko. Photo booth authentic cred semiotics, pickled vegan williamsburg mixtape church-key intelligentsia irony umami PBR. Wayfarers fixie keffiyeh hella ugh, dreamcatcher kale chips banksy Austin swag stumptown deep v tattooed. Mlkshk viral seitan small batch squid cosby sweater, jean shorts neutra. Vegan pitchfork banjo readymade, helvetica ethical bushwick banksy artisan sriracha single-origin coffee. Umami viral fashion axe, marfa DIY banh mi wes anderson master cleanse cosby sweater cray selvage organic actually semiotics.</p>
+ <p>From this distant vantage point, the Earth might not seem of any particular interest. But for us, it's different. Consider again that dot. That's here. That's home. That's us. On it everyone you love, everyone you know, everyone you ever heard of, every human being who ever was, lived out their lives. The aggregate of our joy and suffering, thousands of confident religions, ideologies, and economic doctrines, every hunter and forager, every hero and coward, every creator and destroyer of civilization, every king and peasant, every young couple in love, every mother and father, hopeful child, inventor and explorer, every teacher of morals, every corrupt politician, every "superstar," every "supreme leader," every saint and sinner in the history of our species lived there – on a mote of dust suspended in a sunbeam.</p>
- <p>Yr messenger bag fanny pack small batch, single-origin coffee literally gluten-free farm-to-table 8-bit godard butcher fap actually biodiesel trust fund. Pickled squid cred pug pop-up. Polaroid deep v 90's, +1 godard mumblecore retro next level carles salvia try-hard food truck gluten-free. Four loko post-ironic tofu lomo, narwhal readymade mustache jean shorts letterpress. Leggings put a bird on it farm-to-table jean shorts williamsburg cardigan. Gentrify pug bushwick PBR fixie etsy. PBR banh mi cardigan, cosby sweater master cleanse mixtape fingerstache.</p>
+ <p>Space, the final frontier. These are the voyages of the starship Enterprise. Its five year mission: to explore strange new worlds, to seek out new life and new civilizations, to boldly go where no man has gone before!</p>
</article>
<article>
<h2>This section is shorter than the Readmore minimum</h2>
- <p>Put a bird on it you probably haven't heard of them DIY, vice photo booth terry richardson pickled vegan cray. Pug DIY blog stumptown 3 wolf moon, blue bottle farm-to-table actually banh mi fanny pack quinoa small batch. Mumblecore raw denim hoodie ethnic. Craft beer sriracha mlkshk, organic bicycle rights twee gentrify messenger bag keffiyeh ethical helvetica tumblr. Gluten-free tousled vinyl pop-up. Butcher keytar pickled literally, cosby sweater chambray authentic. Fixie odd future kale chips, church-key pinterest beard pour-over banksy typewriter dreamcatcher squid blue bottle sartorial.</p>
+ <p>Space, the final frontier. These are the voyages of the starship Enterprise. Its five year mission: to explore strange new worlds, to seek out new life and new civilizations, to boldly go where no man has gone before!</p>
</article>
</section>
</div>
- <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
+ <script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="readmore.js"></script>
<script>
$('#info').readmore({
- moreLink: '<a href="#">More examples and options</a>',
- maxHeight: 390,
+ moreLink: '<a href="#">Usage, examples, and options</a>',
+ collapsedHeight: 384,
afterToggle: function(trigger, element, expanded) {
if(! expanded) { // The "Close" link was clicked
- $('html, body').animate( { scrollTop: element.offset().top }, {duration: 100 } );
+ $('html, body').animate({scrollTop: $(element).offset().top}, {duration: 100});
}
}
});
- $('article').readmore({maxHeight: 240});
+ $('article').readmore({speed: 500});
</script>
</body>
</html>
+
diff --git a/gulpfile.js b/gulpfile.js
new file mode 100644
index 0000000..32f7268
--- /dev/null
+++ b/gulpfile.js
@@ -0,0 +1,14 @@
+var gulp = require('gulp'),
+ uglify = require('gulp-uglify'),
+ rename = require('gulp-rename');
+
+gulp.task('compress', function() {
+ gulp.src('readmore.js')
+ .pipe(uglify({
+ mangle: true,
+ compress: true,
+ preserveComments: 'some'
+ }))
+ .pipe(rename('readmore.min.js'))
+ .pipe(gulp.dest('./'));
+});
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..609aad1
--- /dev/null
+++ b/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "Readmore.js",
+ "version": "2.0.0",
+ "description": "A lightweight jQuery plugin for collapsing and expanding long blocks of text with \"Read more\" and \"Close\" links.",
+ "main": "readmore.min.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/jedfoster/Readmore.js.git"
+ },
+ "keywords": [
+ "css",
+ "jquery",
+ "readmore",
+ "expand",
+ "collapse"
+ ],
+ "author": "Jed Foster <jed@jedfoster.com>",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/jedfoster/Readmore.js/issues"
+ },
+ "homepage": "https://github.com/jedfoster/Readmore.js",
+ "devDependencies": {
+ "gulp": "^3.8.10",
+ "gulp-rename": "^1.2.0",
+ "gulp-uglify": "^1.0.2"
+ }
+}
diff --git a/readmore.js b/readmore.js
index 1229fb4..3a993de 100644
--- a/readmore.js
+++ b/readmore.js
@@ -1,192 +1,318 @@
/*!
+ * @preserve
+ *
* Readmore.js jQuery plugin
* Author: @jed_foster
- * Project home: jedfoster.github.io/Readmore.js
+ * Project home: http://jedfoster.github.io/Readmore.js
* Licensed under the MIT license
+ *
+ * Debounce function from http://davidwalsh.name/javascript-debounce-function
*/
-;(function($) {
+/* global jQuery */
+
+(function($) {
+ 'use strict';
var readmore = 'readmore',
defaults = {
speed: 100,
- maxHeight: 200,
+ collapsedHeight: 200,
heightMargin: 16,
moreLink: '<a href="#">Read More</a>',
lessLink: '<a href="#">Close</a>',
embedCSS: true,
- sectionCSS: 'display: block; width: 100%;',
+ blockCSS: 'display: block; width: 100%;',
startOpen: false,
- expandedClass: 'readmore-js-expanded',
- collapsedClass: 'readmore-js-collapsed',
// callbacks
beforeToggle: function(){},
afterToggle: function(){}
},
- cssEmbedded = false;
+ 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;
- function Readmore( element, options ) {
- this.element = element;
+ 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 = element.data('collapsedHeight') || defaultHeight;
+
+ if (!cssMaxHeight) {
+ collapsedHeight = defaultHeight;
+ }
+ else if (cssMaxHeight > collapsedHeight) {
+ collapsedHeight = cssMaxHeight;
+ }
+
+ // Store our measurements.
+ element.data({
+ expandedHeight: expandedHeight,
+ maxHeight: cssMaxHeight,
+ collapsedHeight: collapsedHeight
+ })
+ // and disable any `max-height` property set in CSS
+ .css({
+ maxHeight: 'none'
+ });
+ }
- this.options = $.extend( {}, defaults, options);
+ var resizeBoxes = debounce(function() {
+ $('[data-readmore]').each(function() {
+ var current = $(this),
+ isExpanded = (current.attr('aria-expanded') === 'true');
- $(this.element).data('max-height', this.options.maxHeight);
- $(this.element).data('height-margin', this.options.heightMargin);
+ setBoxHeights(current);
- delete(this.options.maxHeight);
+ 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 +
+ '}';
+ }
- if(this.options.embedCSS && ! cssEmbedded) {
- var styles = '.readmore-js-toggle, .readmore-js-section { ' + this.options.sectionCSS + ' } .readmore-js-section { overflow: hidden; }';
+ // 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');
+ (function(d, u) {
+ var css = d.createElement('style');
css.type = 'text/css';
- if(css.styleSheet) {
- css.styleSheet.cssText = u;
+
+ if (css.styleSheet) {
+ css.styleSheet.cssText = u;
}
else {
- css.appendChild(d.createTextNode(u));
+ css.appendChild(d.createTextNode(u));
}
+
d.getElementsByTagName('head')[0].appendChild(css);
}(document, styles));
- cssEmbedded = true;
+ cssEmbedded[options.selector] = true;
}
+ }
+
+ function Readmore(element, options) {
+ var $this = this;
+
+ this.element = element;
+
+ this.options = $.extend({}, defaults, options);
+
+ $(this.element).data({
+ defaultHeight: this.options.collapsedHeight,
+ heightMargin: this.options.heightMargin
+ });
+
+ embedCSS(this.options);
this._defaults = defaults;
this._name = readmore;
- this.init();
+ window.addEventListener('load', function() {
+ $this.init();
+ });
}
- Readmore.prototype = {
+ Readmore.prototype = {
init: function() {
var $this = this;
$(this.element).each(function() {
- var current = $(this),
- maxHeight = (parseInt(current.css('max-height').replace(/[^-\d\.]/g, ''), 10) > current.data('max-height')) ? parseInt(current.css('max-height').replace(/[^-\d\.]/g, ''), 10) : current.data('max-height'),
- heightMargin = current.data('height-margin');
+ var current = $(this);
- if(current.css('max-height') != 'none') {
- current.css('max-height', 'none');
- }
+ setBoxHeights(current);
- $this.setBoxHeight(current);
+ var collapsedHeight = current.data('collapsedHeight'),
+ heightMargin = current.data('heightMargin');
- if(current.outerHeight(true) <= maxHeight + heightMargin) {
+ if (current.outerHeight(true) <= collapsedHeight + heightMargin) {
// The block is shorter than the limit, so there's no need to truncate it.
return true;
}
else {
- current.addClass('readmore-js-section ' + $this.options.collapsedClass).data('collapsedHeight', maxHeight);
-
- var useLink = $this.options.startOpen ? $this.options.lessLink : $this.options.moreLink;
- current.after($(useLink).on('click', function(event) { $this.toggleSlider(this, current, event) }).addClass('readmore-js-toggle'));
-
- if(!$this.options.startOpen) {
- current.css({height: maxHeight});
+ var id = current.attr('id') || uniqueId(),
+ useLink = $this.options.startOpen ? $this.options.lessLink : $this.options.moreLink;
+
+ current.attr({
+ 'data-readmore': '',
+ 'aria-expanded': false,
+ 'id': id
+ });
+
+ current.after($(useLink)
+ .on('click', function(event) { $this.toggle(this, current[0], event); })
+ .attr({
+ 'data-readmore-toggle': '',
+ 'aria-controls': id
+ }));
+
+ if (! $this.options.startOpen) {
+ current.css({
+ height: collapsedHeight
+ });
}
}
});
- $(window).on('resize', function(event) {
- $this.resizeBoxes();
+ window.addEventListener('resize', function() {
+ resizeBoxes();
});
},
- toggleSlider: function(trigger, element, event)
- {
- event.preventDefault();
+ toggle: function(trigger, element, event) {
+ if (event) {
+ event.preventDefault();
+ }
+
+ if (! trigger) {
+ trigger = $('[aria-controls="' + this.element.id + '"]')[0];
+ }
+
+ if (! element) {
+ element = this.element;
+ }
var $this = this,
- newHeight = newLink = sectionClass = '',
+ $element = $(element),
+ newHeight = '',
+ newLink = '',
expanded = false,
- collapsedHeight = $(element).data('collapsedHeight');
+ collapsedHeight = $element.data('collapsedHeight');
- if ($(element).height() <= collapsedHeight) {
- newHeight = $(element).data('expandedHeight') + 'px';
+ if ($element.height() <= collapsedHeight) {
+ newHeight = $element.data('expandedHeight') + 'px';
newLink = 'lessLink';
expanded = true;
- sectionClass = $this.options.expandedClass;
}
-
else {
newHeight = collapsedHeight;
newLink = 'moreLink';
- sectionClass = $this.options.collapsedClass;
}
// Fire beforeToggle callback
- $this.options.beforeToggle(trigger, element, expanded);
+ // 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`
+ $this.options.beforeToggle(trigger, element, ! expanded);
- $(element).animate({'height': newHeight}, {duration: $this.options.speed, complete: function() {
- // Fire afterToggle callback
- $this.options.afterToggle(trigger, element, expanded);
+ $element.css({'height': newHeight});
- $(trigger).replaceWith($($this.options[newLink]).on('click', function(event) { $this.toggleSlider(this, element, event) }).addClass('readmore-js-toggle'));
+ // Fire afterToggle callback
+ $element.on('transitionend', function() {
+ $this.options.afterToggle(trigger, element, expanded);
- $(this).removeClass($this.options.collapsedClass + ' ' + $this.options.expandedClass).addClass(sectionClass);
- }
+ $(this).attr({
+ 'aria-expanded': expanded
+ }).off('transitionend');
});
- },
-
- setBoxHeight: function(element) {
- var el = element.clone().css({'height': 'auto', 'width': element.width(), 'overflow': 'hidden'}).insertAfter(element),
- height = el.outerHeight(true);
-
- el.remove();
- element.data('expandedHeight', height);
- },
-
- resizeBoxes: function() {
- var $this = this;
-
- $('.readmore-js-section').each(function() {
- var current = $(this);
-
- $this.setBoxHeight(current);
-
- if(current.height() > current.data('expandedHeight') || (current.hasClass($this.options.expandedClass) && current.height() < current.data('expandedHeight')) ) {
- current.css('height', current.data('expandedHeight'));
- }
- });
+ $(trigger).replaceWith($($this.options[newLink])
+ .on('click', function(event) { $this.toggle(this, element, event); })
+ .attr({
+ 'data-readmore-toggle': '',
+ 'aria-controls': $element.attr('id')
+ }));
},
destroy: function() {
- var $this = this;
-
$(this.element).each(function() {
var current = $(this);
- current.removeClass('readmore-js-section ' + $this.options.collapsedClass + ' ' + $this.options.expandedClass).css({'max-height': '', 'height': 'auto'}).next('.readmore-js-toggle').remove();
+ 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;
- if (options === undefined || typeof options === 'object') {
- return this.each(function () {
+
+ $.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);
+ instance.destroy.apply(instance);
}
- $.data(this, 'plugin_' + readmore, new Readmore( this, options ));
+ options.selector = selector;
+
+ $.data(this, 'plugin_' + readmore, new Readmore(this, options));
});
- } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
+ }
+ 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 ) );
+ instance[options].apply(instance, Array.prototype.slice.call(args, 1));
}
});
}
};
+
})(jQuery);
+
diff --git a/readmore.min.js b/readmore.min.js
index ae0ee63..d047897 100644
--- a/readmore.min.js
+++ b/readmore.min.js
@@ -1,7 +1,11 @@
-(function(c){function g(b,a){this.element=b;this.options=c.extend({},h,a);c(this.element).data("max-height",this.options.maxHeight);c(this.element).data("height-margin",this.options.heightMargin);delete this.options.maxHeight;if(this.options.embedCSS&&!k){var d=".readmore-js-toggle, .readmore-js-section { "+this.options.sectionCSS+" } .readmore-js-section { overflow: hidden; }",e=document.createElement("style");e.type="text/css";e.styleSheet?e.styleSheet.cssText=d:e.appendChild(document.createTextNode(d));
-document.getElementsByTagName("head")[0].appendChild(e);k=!0}this._defaults=h;this._name=f;this.init()}var f="readmore",h={speed:100,maxHeight:200,heightMargin:16,moreLink:'<a href="#">Read More</a>',lessLink:'<a href="#">Close</a>',embedCSS:!0,sectionCSS:"display: block; width: 100%;",startOpen:!1,expandedClass:"readmore-js-expanded",collapsedClass:"readmore-js-collapsed",beforeToggle:function(){},afterToggle:function(){}},k=!1;g.prototype={init:function(){var b=this;c(this.element).each(function(){var a=
-c(this),d=a.css("max-height").replace(/[^-\d\.]/g,"")>a.data("max-height")?a.css("max-height").replace(/[^-\d\.]/g,""):a.data("max-height"),e=a.data("height-margin");"none"!=a.css("max-height")&&a.css("max-height","none");b.setBoxHeight(a);if(a.outerHeight(!0)<=d+e)return!0;a.addClass("readmore-js-section "+b.options.collapsedClass).data("collapsedHeight",d);a.after(c(b.options.startOpen?b.options.lessLink:b.options.moreLink).on("click",function(c){b.toggleSlider(this,a,c)}).addClass("readmore-js-toggle"));
-b.options.startOpen||a.css({height:d})});c(window).on("resize",function(a){b.resizeBoxes()})},toggleSlider:function(b,a,d){d.preventDefault();var e=this;d=newLink=sectionClass="";var f=!1;d=c(a).data("collapsedHeight");c(a).height()<=d?(d=c(a).data("expandedHeight")+"px",newLink="lessLink",f=!0,sectionClass=e.options.expandedClass):(newLink="moreLink",sectionClass=e.options.collapsedClass);e.options.beforeToggle(b,a,f);c(a).animate({height:d},{duration:e.options.speed,complete:function(){e.options.afterToggle(b,
-a,f);c(b).replaceWith(c(e.options[newLink]).on("click",function(b){e.toggleSlider(this,a,b)}).addClass("readmore-js-toggle"));c(this).removeClass(e.options.collapsedClass+" "+e.options.expandedClass).addClass(sectionClass)}})},setBoxHeight:function(b){var a=b.clone().css({height:"auto",width:b.width(),overflow:"hidden"}).insertAfter(b),c=a.outerHeight(!0);a.remove();b.data("expandedHeight",c)},resizeBoxes:function(){var b=this;c(".readmore-js-section").each(function(){var a=c(this);b.setBoxHeight(a);
-(a.height()>a.data("expandedHeight")||a.hasClass(b.options.expandedClass)&&a.height()<a.data("expandedHeight"))&&a.css("height",a.data("expandedHeight"))})},destroy:function(){var b=this;c(this.element).each(function(){var a=c(this);a.removeClass("readmore-js-section "+b.options.collapsedClass+" "+b.options.expandedClass).css({"max-height":"",height:"auto"}).next(".readmore-js-toggle").remove();a.removeData()})}};c.fn[f]=function(b){var a=arguments;if(void 0===b||"object"===typeof b)return this.each(function(){if(c.data(this,
-"plugin_"+f)){var a=c.data(this,"plugin_"+f);a.destroy.apply(a)}c.data(this,"plugin_"+f,new g(this,b))});if("string"===typeof b&&"_"!==b[0]&&"init"!==b)return this.each(function(){var d=c.data(this,"plugin_"+f);d instanceof g&&"function"===typeof d[b]&&d[b].apply(d,Array.prototype.slice.call(a,1))})}})(jQuery);
+/*!
+ * @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
+ */
+!function(e){"use strict";function t(e,t,a){var i;return function(){var n=this,o=arguments,r=function(){i=null,a||e.apply(n,o)},s=a&&!i;clearTimeout(i),i=setTimeout(r,t),s&&e.apply(n,o)}}function a(e){var t=++h;return String(null===e?"rmjs-":e)+t}function i(e){var t=e.clone().css({height:"auto",width:e.width(),"max-height":"none",overflow:"hidden"}).insertAfter(e),a=t.outerHeight(!0),i=parseInt(t.css({"max-height":""}).css("max-height").replace(/[^-\d\.]/g,""),10),n=e.data("defaultHeight");t.remove();var o=e.data("collapsedHeight")||n;i?i>o&&(o=i):o=n,e.data({expandedHeight:a,maxHeight:i,collapsedHeight:o}).css("max-height","none")}function n(e){if(!d[e.selector]){var t=" ";e.embedCSS&&""!==e.sectionCSS&&(t+=e.selector+" + [data-readmore-toggle], "+e.selector+"[data-readmore]{"+e.sectionCSS+"}"),t+=e.selector+"[data-readmore]{transition: height "+e.speed+"ms;overflow: hidden;}",function(e,t){var a=e.createElement("style");a.type="text/css",a.styleSheet?a.styleSheet.cssText=t:a.appendChild(e.createTextNode(t)),e.getElementsByTagName("head")[0].appendChild(a)}(document,t),d[e.selector]=!0}}function o(t,a){var i=this;this.element=t,this.options=e.extend({},s,a),e(this.element).data({defaultHeight:this.options.collapsedHeight,heightMargin:this.options.heightMargin}),n(this.options),this._defaults=s,this._name=r,window.addEventListener("load",function(){i.init()})}var r="readmore",s={speed:100,collapsedHeight:200,heightMargin:16,moreLink:'<a href="#">Read More</a>',lessLink:'<a href="#">Close</a>',embedCSS:!0,sectionCSS:"display: block; width: 100%;",startOpen:!1,beforeToggle:function(){},afterToggle:function(){}},d={},h=0,l=t(function(){e("[data-readmore]").each(function(){var t=e(this),a="true"===t.attr("aria-expanded");i(t),t.css("height",t.data(a?"expandedHeight":"collapsedHeight"))})},100);o.prototype={init:function(){var t=this;e(this.element).each(function(){var n=e(this);i(n);var o=n.data("collapsedHeight"),r=n.data("heightMargin");if(n.outerHeight(!0)<=o+r)return!0;var s=n.attr("id")||a(),d=t.options.startOpen?t.options.lessLink:t.options.moreLink;n.attr({"data-readmore":"","aria-expanded":!1,id:s}),n.after(e(d).on("click",function(e){t.toggle(this,n[0],e)}).attr({"data-readmore-toggle":"","aria-controls":s})),t.options.startOpen||n.css({height:o})}),window.addEventListener("resize",function(){l()})},toggle:function(t,a,i){i&&i.preventDefault(),t||(t=e('[aria-controls="'+this.element.id+'"]')[0]),a||(a=this.element);var n=this,o=e(a),r="",s="",d=!1,h=o.data("collapsedHeight");o.height()<=h?(r=o.data("expandedHeight")+"px",s="lessLink",d=!0):(r=h,s="moreLink"),n.options.beforeToggle(t,a,!d),o.css({height:r}),o.on("transitionend",function(){n.options.afterToggle(t,a,d),e(this).attr("aria-expanded",d).off("transitionend")}),e(t).replaceWith(e(n.options[s]).on("click",function(e){n.toggle(this,a,e)}).attr({"data-readmore-toggle":"","aria-controls":o.attr("id")}))},destroy:function(){e(this.element).each(function(){var t=e(this);t.attr({"data-readmore":null,"aria-expanded":null}).css({"max-height":"",height:""}).next("[data-readmore-toggle]").remove(),t.removeData()})}},e.fn.readmore=function(t){var a=arguments,i=this.selector;return t=t||{},"object"==typeof t?this.each(function(){if(e.data(this,"plugin_"+r)){var a=e.data(this,"plugin_"+r);a.destroy.apply(a)}t.selector=i,e.data(this,"plugin_"+r,new o(this,t))}):"string"==typeof t&&"_"!==t[0]&&"init"!==t?this.each(function(){var i=e.data(this,"plugin_"+r);i instanceof o&&"function"==typeof i[t]&&i[t].apply(i,Array.prototype.slice.call(a,1))}):void 0}}(jQuery); \ No newline at end of file