window.ips = {}; /** * Event handling */ // http://www.anujgakhar.com/2013/05/22/cross-browser-event-handling-in-javascript/ window.ips.eventHandler = { on: function (el, ev, fn) { if( window.addEventListener ){ el.addEventListener( ev, fn, false ); } else if( window.attachEvent ){ el.attachEvent( 'on' + ev, fn ); } else { el[ 'on' + ev ] = fn; } }, off: function (el, ev, fn) { if( window.removeEventListener ){ el.removeEventListener( ev, fn, false ); } else if( window.detachEvent ) { el.detachEvent( 'on' + ev, fn ); } else { elem[ 'on' + ev ] = null; } }, stop: function (ev) { var e = ev || window.event; e.cancelBubble = true; if( e.stopPropagation ){ e.stopPropagation(); } } }; /** * Utilities */ window.ips.utils = { /** * Log to console if supported * * @param {string} message Message to log * @returns {void} */ log: function (message) { if( window.console ){ console.log( "(EMBED): " + message ); } }, /** * Error to console if supported * * @param {string} message Message to log * @returns {void} */ error: function (message) { if( window.console ){ console.error( message ); } }, /** * Gets an element based on ID * * @param {string} id Element ID * @returns {element} */ get: function (id) { return document.getElementById( id ); }, /** * Returns the outer height of the element * * @returns {number} */ getObjHeight: function (elem) { return elem.offsetHeight || 0; }, /** * Fade the element in * * @returns void */ fadeIn: function (elem) { elem.style.opacity = 0; var last = +new Date(); var tick = function() { elem.style.opacity = (parseFloat(elem.style.opacity) + (new Date() - last) / 400).toFixed(2); last = +new Date(); if( parseFloat(elem.style.opacity) < 1 ){ ( window.requestAnimationFrame && requestAnimationFrame(tick) ) || setTimeout(tick, 16); } else if (parseFloat(elem.style.opacity) >= 1) { elem.style.opacity = 1; } }; tick(); }, /** * Returns attributes for an element * * @param {element} element DOM element * @returns {object} */ getAttributes: function (element) { if( typeof element == 'undefined' ){ return; } var attributes = {}; if( element.hasAttributes() ){ var _attr = element.attributes; for( var i = 0; i < _attr.length; i++ ){ attributes[ _attr[ i ].name.toLowerCase() ] = _attr[ i ].value; } } return attributes; }, /** * A simple method to insert an element after another element * * @param {element} elem The new element * @param {element} existing The existing element, after which the new will be inserted * @returns {void} */ insertAfter: function (elem, existing) { existing.parentNode.insertBefore( elem, existing.nextSibling ); }, /** * A simple method to insert an element before another element * * @param {element} elem The new element * @param {element} existing The existing element, before which the new will be inserted * @returns {void} */ insertBefore: function (elem, existing) { existing.parentNode.insertBefore( elem, existing ); }, /** * Returns a style property value for the given element and style * * @param {element} elem Element whose style is to be fetched * @param {string} style Property to fetch * @returns {mixed} */ getStyle: function (elem, style) { return window.getComputedStyle( elem, null ).getPropertyValue( style ); }, /* * Cross-browser 'dom ready' event handler to init the page */ /*! contentloaded.min.js - https://github.com/dperini/ContentLoaded - Author: Diego Perini - License: MIT */ contentLoaded: function (b,i) { var j=false,k=true,a=b.document,l=a.documentElement,f=a.addEventListener,h=f?'addEventListener':'attachEvent',n=f?'removeEventListener':'detachEvent',g=f?'':'on',c=function(d){if(d.type=='readystatechange'&&a.readyState!='complete')return;(d.type=='load'?b:a)[n](g+d.type,c,false);if(!j&&(j=true))i.call(b,d.type||d)},m=function(){try{l.doScroll('left')}catch(e){setTimeout(m,50);return}c('poll')};if(a.readyState=='complete')i.call(b,'lazy');else{if(!f&&l.doScroll){try{k=!b.frameElement}catch(e){}if(k)m()}a[h](g+'DOMContentLoaded',c,false);a[h](g+'readystatechange',c,false);b[h](g+'load',c,false)} } }; /* Polyfill for Element.closest, from https://developer.mozilla.org/en-US/docs/Web/API/Element/closest */ if (window.Element && !Element.prototype.closest) { Element.prototype.closest = function(s) { var matches = (this.document || this.ownerDocument).querySelectorAll(s); var i; var el = this; do { i = matches.length; while (--i >= 0 && matches.item(i) !== el) {}; } while ((i < 0) && (el = el.parentElement)); return el; }; } /** * TextOverflowClamp.js * * Updated 2014-05-08 to improve speed and fix some bugs. * * Updated 2013-05-09 to remove jQuery dependancy. * But be careful with webfonts! * * NEW! * - Support for padding. * - Support for nearby floated elements. * - Support for text-indent. */ // bind function support for older browsers without it // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind if (!Function.prototype.bind) { Function.prototype.bind = function (oThis) { if (typeof this !== "function") { // closest thing possible to the ECMAScript 5 internal IsCallable function throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); } var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function () {}, fBound = function () { return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); }; fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; }; } // the actual meat is here (function(w, d){ var clamp, measure, text, lineWidth, lineStart, lineCount, wordStart, line, lineText, wasNewLine, ce = d.createElement.bind(d), ctn = d.createTextNode.bind(d), width, widthChild, newWidthChild; // measurement element is made a child of the clamped element to get it's style measure = ce('span'); (function(s){ s.position = 'absolute'; // prevent page reflow s.whiteSpace = 'pre'; // cross-browser width results s.visibility = 'hidden'; // prevent drawing })(measure.style); // width element calculates the width of each line width = ce('span'); widthChild = ce('span'); widthChild.style.display = 'block'; widthChild.style.overflow = 'hidden'; widthChild.appendChild(ctn("\u2060")); clamp = function (el, lineClamp) { var i; // make sure the element belongs to the document if(!el.ownerDocument || !el.ownerDocument === d) return; // reset to safe starting values lineStart = wordStart = 0; lineCount = 1; wasNewLine = false; //lineWidth = el.clientWidth; lineWidth = []; // get all the text, remove any line changes text = (el.textContent || el.innerText).replace(/\n/g, ' '); // create a child block element that accounts for floats for(i = 1; i < lineClamp; i++) { newWidthChild = widthChild.cloneNode(true); width.appendChild(newWidthChild); if(i === 1) { widthChild.style.textIndent = 0; } } widthChild.style.textIndent = ''; // cleanup newWidthChild = void 0; // remove all content while(el.firstChild) el.removeChild(el.firstChild); // ready for width calculating magic el.appendChild(width); // then start calculating widths of each line for(i = 0; i < lineClamp - 1; i++) { lineWidth.push(width.childNodes[i].clientWidth); } // we are done, no need for this anymore el.removeChild(width); // cleanup the lines while(width.firstChild) width.removeChild(width.firstChild); // add measurement element within so it inherits styles el.appendChild(measure); // http://ejohn.org/blog/search-and-dont-replace/ text.replace(/ /g, function(m, pos) { // ignore any further processing if we have total lines if(lineCount === lineClamp) return; // create a text node and place it in the measurement element measure.appendChild(ctn(text.substr(lineStart, pos - lineStart))); // have we exceeded allowed line width? if(lineWidth[lineCount - 1] <= measure.clientWidth) { if(wasNewLine) { // we have a long word so it gets a line of it's own lineText = text.substr(lineStart, pos + 1 - lineStart); // next line start position lineStart = pos + 1; } else { // grab the text until this word lineText = text.substr(lineStart, wordStart - lineStart); // next line start position lineStart = wordStart; } // create a line element line = ce('span'); // add text to the line element line.appendChild(ctn(lineText)); // add the line element to the container el.appendChild(line); // yes, we created a new line wasNewLine = true; lineCount++; } else { // did not create a new line wasNewLine = false; } // remember last word start position wordStart = pos + 1; // clear measurement element measure.removeChild(measure.firstChild); }); // remove the measurement element from the container el.removeChild(measure); // create the last line element line = ce('span'); // see if we need to add styles if(lineCount === lineClamp) { // give styles required for text-overflow to kick in (function(s) { s.display = 'block'; s.overflow = 'hidden'; s.textIndent = 0; s.textOverflow = 'ellipsis'; s.whiteSpace = 'nowrap'; })(line.style); } // add all remaining text to the line element line.appendChild(ctn(text.substr(lineStart))); // add the line element to the container el.appendChild(line); } w.clamp = clamp; })(window, window.document);