Don't pre-minifiy autoellipsis
This commit is contained in:
parent
2f0ae79ae2
commit
1ef43c33d8
|
@ -33,7 +33,7 @@
|
|||
//= require show-html.js
|
||||
//= require break_string
|
||||
//= require buffered-proxy
|
||||
//= require jquery.autoellipsis-1.0.10.min.js
|
||||
//= require jquery.autoellipsis-1.0.10
|
||||
//= require virtual-dom
|
||||
//= require virtual-dom-amd
|
||||
//= require highlight.js
|
||||
|
|
|
@ -0,0 +1,447 @@
|
|||
/*!
|
||||
|
||||
Copyright (c) 2011 Peter van der Spek
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
(function($) {
|
||||
|
||||
/**
|
||||
* Hash containing mapping of selectors to settings hashes for target selectors that should be live updated.
|
||||
*
|
||||
* @type {Object.<string, Object>}
|
||||
* @private
|
||||
*/
|
||||
var liveUpdatingTargetSelectors = {};
|
||||
|
||||
/**
|
||||
* Interval ID for live updater. Contains interval ID when the live updater interval is active, or is undefined
|
||||
* otherwise.
|
||||
*
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
var liveUpdaterIntervalId;
|
||||
|
||||
/**
|
||||
* Boolean indicating whether the live updater is running.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
var liveUpdaterRunning = false;
|
||||
|
||||
/**
|
||||
* Set of default settings.
|
||||
*
|
||||
* @type {Object.<string, string>}
|
||||
* @private
|
||||
*/
|
||||
var defaultSettings = {
|
||||
ellipsis: '...',
|
||||
setTitle: 'never',
|
||||
live: false
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform ellipsis on selected elements.
|
||||
*
|
||||
* @param {string} selector the inner selector of elements that ellipsis may work on. Inner elements not referred to by this
|
||||
* selector are left untouched.
|
||||
* @param {Object.<string, string>=} options optional options to override default settings.
|
||||
* @return {jQuery} the current jQuery object for chaining purposes.
|
||||
* @this {jQuery} the current jQuery object.
|
||||
*/
|
||||
$.fn.ellipsis = function(selector, options) {
|
||||
var subjectElements, settings;
|
||||
|
||||
subjectElements = $(this);
|
||||
|
||||
// Check for options argument only.
|
||||
if (typeof selector !== 'string') {
|
||||
options = selector;
|
||||
selector = undefined;
|
||||
}
|
||||
|
||||
// Create the settings from the given options and the default settings.
|
||||
settings = $.extend({}, defaultSettings, options);
|
||||
|
||||
// If selector is not set, work on immediate children (default behaviour).
|
||||
settings.selector = selector;
|
||||
|
||||
// Do ellipsis on each subject element.
|
||||
subjectElements.each(function() {
|
||||
var elem = $(this);
|
||||
|
||||
// Do ellipsis on subject element.
|
||||
ellipsisOnElement(elem, settings);
|
||||
});
|
||||
|
||||
// If live option is enabled, add subject elements to live updater. Otherwise remove from live updater.
|
||||
if (settings.live) {
|
||||
addToLiveUpdater(subjectElements.selector, settings);
|
||||
|
||||
} else {
|
||||
removeFromLiveUpdater(subjectElements.selector);
|
||||
}
|
||||
|
||||
// Return jQuery object for chaining.
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Perform ellipsis on the given container.
|
||||
*
|
||||
* @param {jQuery} containerElement jQuery object containing one DOM element to perform ellipsis on.
|
||||
* @param {Object.<string, string>} settings the settings for this ellipsis operation.
|
||||
* @private
|
||||
*/
|
||||
function ellipsisOnElement(containerElement, settings) {
|
||||
var containerData = containerElement.data('jqae');
|
||||
if (!containerData) containerData = {};
|
||||
|
||||
// Check if wrapper div was already created and bound to the container element.
|
||||
var wrapperElement = containerData.wrapperElement;
|
||||
|
||||
// If not, create wrapper element.
|
||||
if (!wrapperElement) {
|
||||
wrapperElement = containerElement.wrapInner('<div/>').find('>div');
|
||||
|
||||
// Wrapper div should not add extra size.
|
||||
wrapperElement.css({
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
border: 0
|
||||
});
|
||||
}
|
||||
|
||||
// Check if the original wrapper element content was already bound to the wrapper element.
|
||||
var wrapperElementData = wrapperElement.data('jqae');
|
||||
if (!wrapperElementData) wrapperElementData = {};
|
||||
|
||||
var wrapperOriginalContent = wrapperElementData.originalContent;
|
||||
|
||||
// If so, clone the original content, re-bind the original wrapper content to the clone, and replace the
|
||||
// wrapper with the clone.
|
||||
if (wrapperOriginalContent) {
|
||||
wrapperElement = wrapperElementData.originalContent.clone(true)
|
||||
.data('jqae', {originalContent: wrapperOriginalContent}).replaceAll(wrapperElement);
|
||||
|
||||
} else {
|
||||
// Otherwise, clone the current wrapper element and bind it as original content to the wrapper element.
|
||||
|
||||
wrapperElement.data('jqae', {originalContent: wrapperElement.clone(true)});
|
||||
}
|
||||
|
||||
// Bind the wrapper element and current container width and height to the container element. Current container
|
||||
// width and height are stored to detect changes to the container size.
|
||||
containerElement.data('jqae', {
|
||||
wrapperElement: wrapperElement,
|
||||
containerWidth: containerElement.width(),
|
||||
containerHeight: containerElement.height()
|
||||
});
|
||||
|
||||
// Calculate with current container element height.
|
||||
var containerElementHeight = containerElement.height();
|
||||
|
||||
// Calculate wrapper offset.
|
||||
var wrapperOffset = (parseInt(containerElement.css('padding-top'), 10) || 0) + (parseInt(containerElement.css('border-top-width'), 10) || 0) - (wrapperElement.offset().top - containerElement.offset().top);
|
||||
|
||||
// Normally the ellipsis characters are applied to the last non-empty text-node in the selected element. If the
|
||||
// selected element becomes empty during ellipsis iteration, the ellipsis characters cannot be applied to that
|
||||
// selected element, and must be deferred to the previous selected element. This parameter keeps track of that.
|
||||
var deferAppendEllipsis = false;
|
||||
|
||||
// Loop through all selected elements in reverse order.
|
||||
var selectedElements = wrapperElement;
|
||||
if (settings.selector) selectedElements = $(wrapperElement.find(settings.selector).get().reverse());
|
||||
|
||||
selectedElements.each(function() {
|
||||
var selectedElement = $(this),
|
||||
originalText = selectedElement.text(),
|
||||
ellipsisApplied = false;
|
||||
|
||||
// Check if we can safely remove the selected element. This saves a lot of unnecessary iterations.
|
||||
if (wrapperElement.innerHeight() - selectedElement.innerHeight() > containerElementHeight + wrapperOffset) {
|
||||
selectedElement.remove();
|
||||
|
||||
} else {
|
||||
// Reverse recursively remove empty elements, until the element that contains a non-empty text-node.
|
||||
removeLastEmptyElements(selectedElement);
|
||||
|
||||
// If the selected element has not become empty, start ellipsis iterations on the selected element.
|
||||
if (selectedElement.contents().length) {
|
||||
|
||||
// If a deffered ellipsis is still pending, apply it now to the last text-node.
|
||||
if (deferAppendEllipsis) {
|
||||
getLastTextNode(selectedElement).get(0).nodeValue += settings.ellipsis;
|
||||
deferAppendEllipsis = false;
|
||||
}
|
||||
|
||||
// Iterate until wrapper element height is less than or equal to the original container element
|
||||
// height plus possible wrapperOffset.
|
||||
while (wrapperElement.innerHeight() > containerElementHeight + wrapperOffset) {
|
||||
// Apply ellipsis on last text node, by removing one word.
|
||||
ellipsisApplied = ellipsisOnLastTextNode(selectedElement);
|
||||
|
||||
// If ellipsis was succesfully applied, remove any remaining empty last elements and append the
|
||||
// ellipsis characters.
|
||||
if (ellipsisApplied) {
|
||||
removeLastEmptyElements(selectedElement);
|
||||
|
||||
// If the selected element is not empty, append the ellipsis characters.
|
||||
if (selectedElement.contents().length) {
|
||||
getLastTextNode(selectedElement).get(0).nodeValue += settings.ellipsis;
|
||||
|
||||
} else {
|
||||
// If the selected element has become empty, defer the appending of the ellipsis characters
|
||||
// to the previous selected element.
|
||||
deferAppendEllipsis = true;
|
||||
selectedElement.remove();
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
// If ellipsis could not be applied, defer the appending of the ellipsis characters to the
|
||||
// previous selected element.
|
||||
deferAppendEllipsis = true;
|
||||
selectedElement.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the "setTitle" property is set to "onEllipsis" and the ellipsis has been applied, or if the
|
||||
// property is set to "always", the add the "title" attribute with the original text. Else remove the
|
||||
// "title" attribute. When the "setTitle" property is set to "never" we do not touch the "title"
|
||||
// attribute.
|
||||
if (((settings.setTitle == 'onEllipsis') && ellipsisApplied) || (settings.setTitle == 'always')) {
|
||||
selectedElement.attr('title', originalText);
|
||||
|
||||
} else if (settings.setTitle != 'never') {
|
||||
selectedElement.removeAttr('title');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs ellipsis on the last text node of the given element. Ellipsis is done by removing a full word.
|
||||
*
|
||||
* @param {jQuery} element jQuery object containing a single DOM element.
|
||||
* @return {boolean} true when ellipsis has been done, false otherwise.
|
||||
* @private
|
||||
*/
|
||||
function ellipsisOnLastTextNode(element) {
|
||||
var lastTextNode = getLastTextNode(element);
|
||||
|
||||
// If the last text node is found, do ellipsis on that node.
|
||||
if (lastTextNode.length) {
|
||||
var text = lastTextNode.get(0).nodeValue;
|
||||
|
||||
// Find last space character, and remove text from there. If no space is found the full remaining text is
|
||||
// removed.
|
||||
var pos = text.lastIndexOf(' ');
|
||||
if (pos > -1) {
|
||||
text = $.trim(text.substring(0, pos));
|
||||
lastTextNode.get(0).nodeValue = text;
|
||||
|
||||
} else {
|
||||
lastTextNode.get(0).nodeValue = '';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last text node of the given element.
|
||||
*
|
||||
* @param {jQuery} element jQuery object containing a single element.
|
||||
* @return {jQuery} jQuery object containing a single text node.
|
||||
* @private
|
||||
*/
|
||||
function getLastTextNode(element) {
|
||||
if (element.contents().length) {
|
||||
|
||||
// Get last child node.
|
||||
var contents = element.contents();
|
||||
var lastNode = contents.eq(contents.length - 1);
|
||||
|
||||
// If last node is a text node, return it.
|
||||
if (lastNode.filter(textNodeFilter).length) {
|
||||
return lastNode;
|
||||
|
||||
} else {
|
||||
// Else it is an element node, and we recurse into it.
|
||||
|
||||
return getLastTextNode(lastNode);
|
||||
}
|
||||
|
||||
} else {
|
||||
// If there is no last child node, we append an empty text node and return that. Normally this should not
|
||||
// happen, as we test for emptiness before calling getLastTextNode.
|
||||
|
||||
element.append('');
|
||||
var contents = element.contents();
|
||||
return contents.eq(contents.length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove last empty elements. This is done recursively until the last element contains a non-empty text node.
|
||||
*
|
||||
* @param {jQuery} element jQuery object containing a single element.
|
||||
* @return {boolean} true when elements have been removed, false otherwise.
|
||||
* @private
|
||||
*/
|
||||
function removeLastEmptyElements(element) {
|
||||
if (element.contents().length) {
|
||||
|
||||
// Get last child node.
|
||||
var contents = element.contents();
|
||||
var lastNode = contents.eq(contents.length - 1);
|
||||
|
||||
// If last child node is a text node, check for emptiness.
|
||||
if (lastNode.filter(textNodeFilter).length) {
|
||||
var text = lastNode.get(0).nodeValue;
|
||||
text = $.trim(text);
|
||||
|
||||
if (text == '') {
|
||||
// If empty, remove the text node.
|
||||
lastNode.remove();
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
// If the last child node is an element node, remove the last empty child nodes on that node.
|
||||
while (removeLastEmptyElements(lastNode)) {
|
||||
}
|
||||
|
||||
// If the last child node contains no more child nodes, remove the last child node.
|
||||
if (lastNode.contents().length) {
|
||||
return false;
|
||||
|
||||
} else {
|
||||
lastNode.remove();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter for testing on text nodes.
|
||||
*
|
||||
* @return {boolean} true when this node is a text node, false otherwise.
|
||||
* @this {Node}
|
||||
* @private
|
||||
*/
|
||||
function textNodeFilter() {
|
||||
return this.nodeType === 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add target selector to hash of target selectors. If this is the first target selector added, start the live
|
||||
* updater.
|
||||
*
|
||||
* @param {string} targetSelector the target selector to run the live updater for.
|
||||
* @param {Object.<string, string>} settings the settings to apply on this target selector.
|
||||
* @private
|
||||
*/
|
||||
function addToLiveUpdater(targetSelector, settings) {
|
||||
// Store target selector with its settings.
|
||||
liveUpdatingTargetSelectors[targetSelector] = settings;
|
||||
|
||||
// If the live updater has not yet been started, start it now.
|
||||
if (!liveUpdaterIntervalId) {
|
||||
liveUpdaterIntervalId = window.setInterval(function() {
|
||||
doLiveUpdater();
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the target selector from the hash of target selectors. It this is the last remaining target selector
|
||||
* being removed, stop the live updater.
|
||||
*
|
||||
* @param {string} targetSelector the target selector to stop running the live updater for.
|
||||
* @private
|
||||
*/
|
||||
function removeFromLiveUpdater(targetSelector) {
|
||||
// If the hash contains the target selector, remove it.
|
||||
if (liveUpdatingTargetSelectors[targetSelector]) {
|
||||
delete liveUpdatingTargetSelectors[targetSelector];
|
||||
|
||||
// If no more target selectors are in the hash, stop the live updater.
|
||||
if (!liveUpdatingTargetSelectors.length) {
|
||||
if (liveUpdaterIntervalId) {
|
||||
window.clearInterval(liveUpdaterIntervalId);
|
||||
liveUpdaterIntervalId = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Run the live updater. The live updater is periodically run to check if its monitored target selectors require
|
||||
* re-applying of the ellipsis.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function doLiveUpdater() {
|
||||
// If the live updater is already running, skip this time. We only want one instance running at a time.
|
||||
if (!liveUpdaterRunning) {
|
||||
liveUpdaterRunning = true;
|
||||
|
||||
// Loop through target selectors.
|
||||
for (var targetSelector in liveUpdatingTargetSelectors) {
|
||||
$(targetSelector).each(function() {
|
||||
var containerElement, containerData;
|
||||
|
||||
containerElement = $(this);
|
||||
containerData = containerElement.data('jqae');
|
||||
|
||||
// If container element dimensions have changed, or the container element is new, run ellipsis on
|
||||
// that container element.
|
||||
if ((containerData.containerWidth != containerElement.width()) ||
|
||||
(containerData.containerHeight != containerElement.height())) {
|
||||
ellipsisOnElement(containerElement, liveUpdatingTargetSelectors[targetSelector]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
liveUpdaterRunning = false;
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
|
@ -1,23 +0,0 @@
|
|||
/*!
|
||||
|
||||
Copyright (c) 2011 Peter van der Spek
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/(function(a){function m(){if(!d){d=!0;for(var c in b)a(c).each(function(){var d,e;d=a(this),e=d.data("jqae"),(e.containerWidth!=d.width()||e.containerHeight!=d.height())&&f(d,b[c])});d=!1}}function l(a){b[a]&&(delete b[a],b.length||c&&(window.clearInterval(c),c=undefined))}function k(a,d){b[a]=d,c||(c=window.setInterval(function(){m()},200))}function j(){return this.nodeType===3}function i(b){if(b.contents().length){var c=b.contents(),d=c.eq(c.length-1);if(d.filter(j).length){var e=d.get(0).nodeValue;e=a.trim(e);if(e==""){d.remove();return!0}return!1}while(i(d));if(d.contents().length)return!1;d.remove();return!0}return!1}function h(a){if(a.contents().length){var b=a.contents(),c=b.eq(b.length-1);return c.filter(j).length?c:h(c)}a.append("");var b=a.contents();return b.eq(b.length-1)}function g(b){var c=h(b);if(c.length){var d=c.get(0).nodeValue,e=d.lastIndexOf(" ");e>-1?(d=a.trim(d.substring(0,e)),c.get(0).nodeValue=d):c.get(0).nodeValue="";return!0}return!1}function f(b,c){var d=b.data("jqae");d||(d={});var e=d.wrapperElement;e||(e=b.wrapInner("<div/>").find(">div"),e.css({margin:0,padding:0,border:0}));var f=e.data("jqae");f||(f={});var j=f.originalContent;j?e=f.originalContent.clone(!0).data("jqae",{originalContent:j}).replaceAll(e):e.data("jqae",{originalContent:e.clone(!0)}),b.data("jqae",{wrapperElement:e,containerWidth:b.width(),containerHeight:b.height()});var k=b.height(),l=(parseInt(b.css("padding-top"),10)||0)+(parseInt(b.css("border-top-width"),10)||0)-(e.offset().top-b.offset().top),m=!1,n=e;c.selector&&(n=a(e.find(c.selector).get().reverse())),n.each(function(){var b=a(this),d=b.text(),f=!1;if(e.innerHeight()-b.innerHeight()>k+l)b.remove();else{i(b);if(b.contents().length){m&&(h(b).get(0).nodeValue+=c.ellipsis,m=!1);while(e.innerHeight()>k+l){f=g(b);if(!f){m=!0,b.remove();break}i(b);if(b.contents().length)h(b).get(0).nodeValue+=c.ellipsis;else{m=!0,b.remove();break}}c.setTitle=="onEllipsis"&&f||c.setTitle=="always"?b.attr("title",d):c.setTitle!="never"&&b.removeAttr("title")}}})}var b={},c,d=!1,e={ellipsis:"...",setTitle:"never",live:!1};a.fn.ellipsis=function(b,c){var d,g;d=a(this),typeof b!="string"&&(c=b,b=undefined),g=a.extend({},e,c),g.selector=b,d.each(function(){var b=a(this);f(b,g)}),g.live?k(d.selector,g):l(d.selector);return this}})(jQuery)
|
Loading…
Reference in New Issue