This repository has been archived on 2018-10-12. You can view files and clone it, but cannot push or open issues or pull requests.
node-task/public/js/ink.imagequery.js

187 lines
6.4 KiB
JavaScript
Raw Normal View History

2014-09-24 17:57:25 -04:00
/**
* Responsive image loading
* @module Ink.UI.ImageQuery_1
* @version 1
*/
Ink.createModule('Ink.UI.ImageQuery', '1', ['Ink.UI.Common_1','Ink.Dom.Event_1','Ink.Dom.Element_1','Ink.Util.Array_1'], function(Common, Event, Element, InkArray ) {
'use strict';
/**
* @class Ink.UI.ImageQuery
* @constructor
* @version 1
*
* @param {String|DOMElement} selector Selector or element
* @param {Object} [options] Options object
* @param {String|Function} [options.src] String or Callback function (that returns a string) with the path to be used to get the images.
* @param {String|Function} [options.retina] String or Callback function (that returns a string) with the path to be used to get RETINA specific images.
* @param {Array} [options.queries] Array of queries
* @param {String} [options.queries.label] Label of the query. Ex. 'small'
* @param {Number} [options.queries.width] Min-width to use this query
* @param {Function} [options.onLoad] Date format string
*
* @sample Ink_UI_ImageQuery_1.html
*/
function ImageQuery() {
Common.BaseUIComponent.apply(this, arguments);
}
ImageQuery._name = 'ImageQuery_1';
ImageQuery._optionDefinition = {
src: ['String'],
retina: ['String', undefined],
queries: ['Object'],
onLoad: ['Function', null]
};
ImageQuery.prototype = {
/**
* Init function called by the constructor
*
* @method _init
* @private
*/
_init: function(){
// /**
// * Get elements, create more ImageQueries if selector finds more than one
// *
// * [improvement] This is a useful pattern. More UI modules could use it.
// */
// this._element = Common.elsOrSelector(selector, 'Ink.UI.ImageQuery', /*required=*/true);
// // In case we have several elements
// for (var i = 1 /* start from second element*/; i < this._element.length; i++) {
// new ImageQuery(this._element[i], options);
// }
// this._element = this._element[0];
/**
* Determining the original basename (with the querystring) of the file.
*/
var pos;
if( (pos=this._element.src.lastIndexOf('?')) !== -1 ){
var search = this._element.src.substr(pos);
this._filename = this._element.src.replace(search,'').split('/').pop()+search;
} else {
this._filename = this._element.src.split('/').pop();
}
if (!this._options.queries) { this._options.queries = []; }
// Sort queries by width, in descendant order.
this._options.queries = InkArray.sortMulti(this._options.queries, 'width').reverse();
if( typeof this._options.onLoad === 'function' ){
Event.observe(this._element, 'onload', Ink.bindEvent(this._onLoad, this));
}
// Imediate call to apply the right images based on the current viewport
this._onResize();
Event.observe(window, 'resize', Event.throttle(Ink.bindMethod(this, '_onResize'), 400));
},
/**
* Handles the resize event (as specified in the _init function)
*
* @method _onResize
* @private
*/
_onResize: function(){
if( !this._options.queries.length ){
return;
}
var current = this._findCurrentQuery();
/**
* Choosing the right src. The rule is:
*
* "If there is specifically defined in the query object, use that. Otherwise uses the global src."
*
* The above rule applies to a retina src.
*/
var src = current.src || this._options.src;
if ( window.devicePixelRatio > 1 && (this._options.retina !== undefined) ) {
src = current.retina || this._options.retina;
}
/**
* Injects the file variable for usage in the 'templating system' below
*/
current.file = this._filename;
/**
* Since we allow the src to be a callback, let's run it and get the results.
* For the inside, we're passing the element (img) being processed and the object of the selected query.
*/
if( typeof src === 'function' ){
src = src.apply(this,[this._element,current]);
if( typeof src !== 'string' ){
throw '[ImageQuery] :: "src" callback does not return a string';
}
}
/**
* Replace the values of the existing properties on the query object (except src and retina) in the
* defined src and/or retina.
*/
src = src.replace(/{:(.*?)}/g, function(_, prop) {
return current[prop];
});
this._element.src = src;
// Removes the injected file property
delete current.file;
},
/**
* Queries are in a descendant order. We want to find the query with the highest width that fits the viewport, therefore the first one.
*/
_findCurrentQuery: function () {
/**
* Gets viewport width
*/
var viewportWidth = window.innerWidth ||
document.documentElement.clientWidth ||
document.body.clientWidth;
var queries = this._options.queries;
var last = queries.length - 1;
for( var query=0; query < last; query+=1 ){
if (queries[query].width <= viewportWidth){
return queries[query];
}
}
return queries[last];
},
/**
* Handles the element loading (img onload) event
*
* @method _onLoad
* @private
*/
_onLoad: function(){
/**
* Since we allow a callback for this let's run it.
*/
this._options.onLoad.call(this);
}
};
Common.createUIComponent(ImageQuery);
return ImageQuery;
});