4026 lines
104 KiB
JavaScript
4026 lines
104 KiB
JavaScript
/*
|
|
Kube UI Framework
|
|
Version 7.2.1
|
|
Updated: November 10, 2018
|
|
|
|
http://imperavi.com/kube/
|
|
|
|
Copyright (c) 2009-2018, Imperavi LLC.
|
|
License: MIT
|
|
*/
|
|
(function() {
|
|
var Ajax = {};
|
|
|
|
Ajax.settings = {};
|
|
Ajax.post = function(options) { return new AjaxRequest('post', options); };
|
|
Ajax.get = function(options) { return new AjaxRequest('get', options); };
|
|
|
|
var AjaxRequest = function(method, options)
|
|
{
|
|
var defaults = {
|
|
method: method,
|
|
url: '',
|
|
before: function() {},
|
|
success: function() {},
|
|
error: function() {},
|
|
data: false,
|
|
async: true,
|
|
headers: {}
|
|
};
|
|
|
|
this.p = this.extend(defaults, options);
|
|
this.p = this.extend(this.p, Ajax.settings);
|
|
this.p.method = this.p.method.toUpperCase();
|
|
|
|
this.prepareData();
|
|
|
|
this.xhr = new XMLHttpRequest();
|
|
this.xhr.open(this.p.method, this.p.url, this.p.async);
|
|
|
|
this.setHeaders();
|
|
|
|
var before = (typeof this.p.before === 'function') ? this.p.before(this.xhr) : true;
|
|
if (before !== false)
|
|
{
|
|
this.send();
|
|
}
|
|
};
|
|
|
|
AjaxRequest.prototype = {
|
|
extend: function(obj1, obj2)
|
|
{
|
|
if (obj2) for (var name in obj2) { obj1[name] = obj2[name]; }
|
|
return obj1;
|
|
},
|
|
prepareData: function()
|
|
{
|
|
if (this.p.method === 'POST' && !this.isFormData()) this.p.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
if (typeof this.p.data === 'object' && !this.isFormData()) this.p.data = this.toParams(this.p.data);
|
|
if (this.p.method === 'GET') this.p.url = (this.p.data) ? this.p.url + '?' + this.p.data : this.p.url;
|
|
},
|
|
setHeaders: function()
|
|
{
|
|
this.xhr.setRequestHeader('X-Requested-With', this.p.headers['X-Requested-With'] || 'XMLHttpRequest');
|
|
for (var name in this.p.headers)
|
|
{
|
|
this.xhr.setRequestHeader(name, this.p.headers[name]);
|
|
}
|
|
},
|
|
isFormData: function()
|
|
{
|
|
return (typeof window.FormData !== 'undefined' && this.p.data instanceof window.FormData);
|
|
},
|
|
isComplete: function()
|
|
{
|
|
return !(this.xhr.status < 200 || this.xhr.status >= 300 && this.xhr.status !== 304);
|
|
},
|
|
send: function()
|
|
{
|
|
if (this.p.async)
|
|
{
|
|
this.xhr.onload = this.loaded.bind(this);
|
|
this.xhr.send(this.p.data);
|
|
}
|
|
else
|
|
{
|
|
this.xhr.send(this.p.data);
|
|
this.loaded.call(this);
|
|
}
|
|
},
|
|
loaded: function()
|
|
{
|
|
if (this.isComplete())
|
|
{
|
|
var response = this.xhr.response;
|
|
var json = this.parseJson(response);
|
|
response = (json) ? json : response;
|
|
|
|
if (typeof this.p.success === 'function') this.p.success(response, this.xhr);
|
|
}
|
|
else
|
|
{
|
|
if (typeof this.p.error === 'function') this.p.error(this.xhr.statusText);
|
|
}
|
|
},
|
|
parseJson: function(str)
|
|
{
|
|
try {
|
|
var o = JSON.parse(str);
|
|
if (o && typeof o === 'object')
|
|
{
|
|
return o;
|
|
}
|
|
|
|
} catch (e) {}
|
|
|
|
return false;
|
|
},
|
|
toParams: function (obj)
|
|
{
|
|
return Object.keys(obj).map(
|
|
function(k){ return encodeURIComponent(k) + '=' + encodeURIComponent(obj[k]); }
|
|
).join('&');
|
|
}
|
|
};
|
|
var DomCache = [0];
|
|
var DomExpando = 'data' + +new Date();
|
|
var DomHClass = 'is-hidden';
|
|
var DomHMClass = 'is-hidden-mobile';
|
|
|
|
var Dom = function(selector, context)
|
|
{
|
|
return this.parse(selector, context);
|
|
};
|
|
|
|
Dom.ready = function(fn)
|
|
{
|
|
if (document.readyState != 'loading') fn();
|
|
else document.addEventListener('DOMContentLoaded', fn);
|
|
};
|
|
|
|
Dom.prototype = {
|
|
get dom()
|
|
{
|
|
return true;
|
|
},
|
|
get length()
|
|
{
|
|
return this.nodes.length;
|
|
},
|
|
parse: function(selector, context)
|
|
{
|
|
var nodes;
|
|
var reHtmlTest = /^\s*<(\w+|!)[^>]*>/;
|
|
|
|
if (!selector)
|
|
{
|
|
nodes = [];
|
|
}
|
|
else if (selector.dom)
|
|
{
|
|
this.nodes = selector.nodes;
|
|
return selector;
|
|
}
|
|
else if (typeof selector !== 'string')
|
|
{
|
|
if (selector.nodeType && selector.nodeType === 11)
|
|
{
|
|
nodes = selector.childNodes;
|
|
}
|
|
else
|
|
{
|
|
nodes = (selector.nodeType || selector === window) ? [selector] : selector;
|
|
}
|
|
}
|
|
else if (reHtmlTest.test(selector))
|
|
{
|
|
nodes = this.create(selector);
|
|
}
|
|
else
|
|
{
|
|
nodes = this._query(selector, context);
|
|
}
|
|
|
|
this.nodes = this._slice(nodes);
|
|
},
|
|
create: function(html)
|
|
{
|
|
if (/^<(\w+)\s*\/?>(?:<\/\1>|)$/.test(html))
|
|
{
|
|
return [document.createElement(RegExp.$1)];
|
|
}
|
|
|
|
var elements = [];
|
|
var container = document.createElement('div');
|
|
var children = container.childNodes;
|
|
|
|
container.innerHTML = html;
|
|
|
|
for (var i = 0, l = children.length; i < l; i++)
|
|
{
|
|
elements.push(children[i]);
|
|
}
|
|
|
|
return elements;
|
|
},
|
|
|
|
// add
|
|
add: function(nodes)
|
|
{
|
|
this.nodes = this.nodes.concat(this._toArray(nodes));
|
|
},
|
|
|
|
// get
|
|
get: function(index)
|
|
{
|
|
return this.nodes[(index || 0)] || false;
|
|
},
|
|
getAll: function()
|
|
{
|
|
return this.nodes;
|
|
},
|
|
eq: function(index)
|
|
{
|
|
return new Dom(this.nodes[index]);
|
|
},
|
|
first: function()
|
|
{
|
|
return new Dom(this.nodes[0]);
|
|
},
|
|
last: function()
|
|
{
|
|
return new Dom(this.nodes[this.nodes.length - 1]);
|
|
},
|
|
contents: function()
|
|
{
|
|
return this.get().childNodes;
|
|
},
|
|
|
|
// loop
|
|
each: function(callback)
|
|
{
|
|
var len = this.nodes.length;
|
|
for (var i = 0; i < len; i++)
|
|
{
|
|
callback.call(this, (this.nodes[i].dom) ? this.nodes[i].get() : this.nodes[i], i);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
// traversing
|
|
is: function(selector)
|
|
{
|
|
return (this.filter(selector).length > 0);
|
|
},
|
|
filter: function (selector)
|
|
{
|
|
var callback;
|
|
if (selector === undefined)
|
|
{
|
|
return this;
|
|
}
|
|
else if (typeof selector === 'function')
|
|
{
|
|
callback = selector;
|
|
}
|
|
else
|
|
{
|
|
callback = function(node)
|
|
{
|
|
if (selector instanceof Node)
|
|
{
|
|
return (selector === node);
|
|
}
|
|
else if (selector && selector.dom)
|
|
{
|
|
return ((selector.nodes).indexOf(node) !== -1);
|
|
}
|
|
else
|
|
{
|
|
node.matches = node.matches || node.msMatchesSelector || node.webkitMatchesSelector;
|
|
return (node.nodeType === 1) ? node.matches(selector || '*') : false;
|
|
}
|
|
};
|
|
}
|
|
|
|
return new Dom(this.nodes.filter(callback));
|
|
},
|
|
not: function(filter)
|
|
{
|
|
return this.filter(function(node)
|
|
{
|
|
return !new Dom(node).is(filter || true);
|
|
});
|
|
},
|
|
find: function(selector)
|
|
{
|
|
var nodes = [];
|
|
this.each(function(node)
|
|
{
|
|
var ns = this._query(selector || '*', node);
|
|
for (var i = 0; i < ns.length; i++)
|
|
{
|
|
nodes.push(ns[i]);
|
|
}
|
|
});
|
|
|
|
return new Dom(nodes);
|
|
},
|
|
children: function(selector)
|
|
{
|
|
var nodes = [];
|
|
this.each(function(node)
|
|
{
|
|
if (node.children)
|
|
{
|
|
var ns = node.children;
|
|
for (var i = 0; i < ns.length; i++)
|
|
{
|
|
nodes.push(ns[i]);
|
|
}
|
|
}
|
|
});
|
|
|
|
return new Dom(nodes).filter(selector);
|
|
},
|
|
parent: function(selector)
|
|
{
|
|
var nodes = [];
|
|
this.each(function(node)
|
|
{
|
|
if (node.parentNode) nodes.push(node.parentNode);
|
|
});
|
|
|
|
return new Dom(nodes).filter(selector);
|
|
},
|
|
parents: function(selector, context)
|
|
{
|
|
context = this._getContext(context);
|
|
|
|
var nodes = [];
|
|
this.each(function(node)
|
|
{
|
|
var parent = node.parentNode;
|
|
while (parent && parent !== context)
|
|
{
|
|
if (selector)
|
|
{
|
|
if (new Dom(parent).is(selector)) { nodes.push(parent); }
|
|
}
|
|
else
|
|
{
|
|
nodes.push(parent);
|
|
}
|
|
|
|
parent = parent.parentNode;
|
|
}
|
|
});
|
|
|
|
return new Dom(nodes);
|
|
},
|
|
closest: function(selector, context)
|
|
{
|
|
context = this._getContext(context);
|
|
selector = (selector.dom) ? selector.get() : selector;
|
|
|
|
var nodes = [];
|
|
var isNode = (selector && selector.nodeType);
|
|
this.each(function(node)
|
|
{
|
|
do {
|
|
if ((isNode && node === selector) || new Dom(node).is(selector)) return nodes.push(node);
|
|
} while ((node = node.parentNode) && node !== context);
|
|
});
|
|
|
|
return new Dom(nodes);
|
|
},
|
|
next: function(selector)
|
|
{
|
|
return this._getSibling(selector, 'nextSibling');
|
|
},
|
|
nextElement: function(selector)
|
|
{
|
|
return this._getSibling(selector, 'nextElementSibling');
|
|
},
|
|
prev: function(selector)
|
|
{
|
|
return this._getSibling(selector, 'previousSibling');
|
|
},
|
|
prevElement: function(selector)
|
|
{
|
|
return this._getSibling(selector, 'previousElementSibling');
|
|
},
|
|
|
|
// css
|
|
css: function(name, value)
|
|
{
|
|
if (value === undefined && (typeof name !== 'object'))
|
|
{
|
|
var node = this.get();
|
|
if (name === 'width' || name === 'height')
|
|
{
|
|
return (node.style) ? this._getHeightOrWidth(name, node, false) + 'px' : undefined;
|
|
}
|
|
else
|
|
{
|
|
return (node.style) ? getComputedStyle(node, null)[name] : undefined;
|
|
}
|
|
}
|
|
|
|
// set
|
|
return this.each(function(node)
|
|
{
|
|
var obj = {};
|
|
if (typeof name === 'object') obj = name;
|
|
else obj[name] = value;
|
|
|
|
for (var key in obj)
|
|
{
|
|
if (node.style) node.style[key] = obj[key];
|
|
}
|
|
});
|
|
},
|
|
|
|
// attr
|
|
attr: function(name, value, data)
|
|
{
|
|
data = (data) ? 'data-' : '';
|
|
|
|
if (value === undefined && (typeof name !== 'object'))
|
|
{
|
|
var node = this.get();
|
|
if (node && node.nodeType !== 3)
|
|
{
|
|
return (name === 'checked') ? node.checked : this._getBooleanFromStr(node.getAttribute(data + name));
|
|
}
|
|
else return;
|
|
}
|
|
|
|
// set
|
|
return this.each(function(node)
|
|
{
|
|
var obj = {};
|
|
if (typeof name === 'object') obj = name;
|
|
else obj[name] = value;
|
|
|
|
for (var key in obj)
|
|
{
|
|
if (node.nodeType !== 3)
|
|
{
|
|
if (key === 'checked') node.checked = obj[key];
|
|
else node.setAttribute(data + key, obj[key]);
|
|
}
|
|
}
|
|
});
|
|
},
|
|
data: function(name, value)
|
|
{
|
|
if (name === undefined)
|
|
{
|
|
var reDataAttr = /^data\-(.+)$/;
|
|
var attrs = this.get().attributes;
|
|
|
|
var data = {};
|
|
var replacer = function (g) { return g[1].toUpperCase(); };
|
|
|
|
for (var key in attrs)
|
|
{
|
|
if (attrs[key] && reDataAttr.test(attrs[key].nodeName))
|
|
{
|
|
var dataName = attrs[key].nodeName.match(reDataAttr)[1];
|
|
var val = attrs[key].value;
|
|
dataName = dataName.replace(/-([a-z])/g, replacer);
|
|
|
|
if (this._isObjectString(val)) val = this._toObject(val);
|
|
else val = (this._isNumber(val)) ? parseFloat(val) : this._getBooleanFromStr(val);
|
|
|
|
data[dataName] = val;
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
return this.attr(name, value, true);
|
|
},
|
|
val: function(value)
|
|
{
|
|
if (value === undefined)
|
|
{
|
|
var el = this.get();
|
|
if (el.type && el.type === 'checkbox') return el.checked;
|
|
else return el.value;
|
|
}
|
|
|
|
return this.each(function(node)
|
|
{
|
|
node.value = value;
|
|
});
|
|
},
|
|
removeAttr: function(value)
|
|
{
|
|
return this.each(function(node)
|
|
{
|
|
var rmAttr = function(name) { if (node.nodeType !== 3) node.removeAttribute(name); };
|
|
value.split(' ').forEach(rmAttr);
|
|
});
|
|
},
|
|
removeData: function(value)
|
|
{
|
|
return this.each(function(node)
|
|
{
|
|
var rmData = function(name) { if (node.nodeType !== 3) node.removeAttribute('data-' + name); };
|
|
value.split(' ').forEach(rmData);
|
|
});
|
|
},
|
|
|
|
// dataset/dataget
|
|
dataset: function(key, value)
|
|
{
|
|
return this.each(function(node)
|
|
{
|
|
DomCache[this.dataindex(node)][key] = value;
|
|
});
|
|
},
|
|
dataget: function(key)
|
|
{
|
|
return DomCache[this.dataindex(this.get())][key];
|
|
},
|
|
dataindex: function(el)
|
|
{
|
|
var cacheIndex = el[DomExpando];
|
|
var nextCacheIndex = DomCache.length;
|
|
|
|
if (!cacheIndex)
|
|
{
|
|
cacheIndex = el[DomExpando] = nextCacheIndex;
|
|
DomCache[cacheIndex] = {};
|
|
}
|
|
|
|
return cacheIndex;
|
|
},
|
|
|
|
|
|
// class
|
|
addClass: function(value)
|
|
{
|
|
return this._eachClass(value, 'add');
|
|
},
|
|
removeClass: function(value)
|
|
{
|
|
return this._eachClass(value, 'remove');
|
|
},
|
|
toggleClass: function(value)
|
|
{
|
|
return this._eachClass(value, 'toggle');
|
|
},
|
|
hasClass: function(value)
|
|
{
|
|
return this.nodes.some(function(node)
|
|
{
|
|
return (node.classList) ? node.classList.contains(value) : false;
|
|
});
|
|
},
|
|
|
|
// html & text
|
|
empty: function()
|
|
{
|
|
return this.each(function(node)
|
|
{
|
|
node.innerHTML = '';
|
|
});
|
|
},
|
|
html: function(html)
|
|
{
|
|
return (html === undefined) ? (this.get().innerHTML || '') : this.empty().append(html);
|
|
},
|
|
text: function(text)
|
|
{
|
|
return (text === undefined) ? (this.get().textContent || '') : this.each(function(node) { node.textContent = text; });
|
|
},
|
|
|
|
// manipulation
|
|
after: function(html)
|
|
{
|
|
return this._inject(html, function(frag, node)
|
|
{
|
|
if (typeof frag === 'string')
|
|
{
|
|
node.insertAdjacentHTML('afterend', frag);
|
|
}
|
|
else
|
|
{
|
|
var elms = (frag instanceof Node) ? [frag] : this._toArray(frag).reverse();
|
|
for (var i = 0; i < elms.length; i++)
|
|
{
|
|
node.parentNode.insertBefore(elms[i], node.nextSibling);
|
|
}
|
|
}
|
|
|
|
return node;
|
|
|
|
});
|
|
},
|
|
before: function(html)
|
|
{
|
|
return this._inject(html, function(frag, node)
|
|
{
|
|
if (typeof frag === 'string')
|
|
{
|
|
node.insertAdjacentHTML('beforebegin', frag);
|
|
}
|
|
else
|
|
{
|
|
var elms = (frag instanceof Node) ? [frag] : this._toArray(frag);
|
|
for (var i = 0; i < elms.length; i++)
|
|
{
|
|
node.parentNode.insertBefore(elms[i], node);
|
|
}
|
|
}
|
|
|
|
return node;
|
|
});
|
|
},
|
|
append: function(html)
|
|
{
|
|
return this._inject(html, function(frag, node)
|
|
{
|
|
if (typeof frag === 'string' || typeof frag === 'number')
|
|
{
|
|
node.insertAdjacentHTML('beforeend', frag);
|
|
}
|
|
else
|
|
{
|
|
var elms = (frag instanceof Node) ? [frag] : this._toArray(frag);
|
|
for (var i = 0; i < elms.length; i++)
|
|
{
|
|
node.appendChild(elms[i]);
|
|
}
|
|
}
|
|
|
|
return node;
|
|
});
|
|
},
|
|
prepend: function(html)
|
|
{
|
|
return this._inject(html, function(frag, node)
|
|
{
|
|
if (typeof frag === 'string' || typeof frag === 'number')
|
|
{
|
|
node.insertAdjacentHTML('afterbegin', frag);
|
|
}
|
|
else
|
|
{
|
|
var elms = (frag instanceof Node) ? [frag] : this._toArray(frag).reverse();
|
|
for (var i = 0; i < elms.length; i++)
|
|
{
|
|
node.insertBefore(elms[i], node.firstChild);
|
|
}
|
|
}
|
|
|
|
return node;
|
|
});
|
|
},
|
|
wrap: function(html)
|
|
{
|
|
return this._inject(html, function(frag, node)
|
|
{
|
|
var wrapper = (typeof frag === 'string' || typeof frag === 'number') ? this.create(frag)[0] : (frag instanceof Node) ? frag : this._toArray(frag)[0];
|
|
|
|
if (node.parentNode)
|
|
{
|
|
node.parentNode.insertBefore(wrapper, node);
|
|
}
|
|
|
|
wrapper.appendChild(node);
|
|
|
|
return new Dom(wrapper);
|
|
|
|
});
|
|
},
|
|
unwrap: function()
|
|
{
|
|
return this.each(function(node)
|
|
{
|
|
var $node = new Dom(node);
|
|
|
|
return $node.replaceWith($node.contents());
|
|
});
|
|
},
|
|
replaceWith: function(html)
|
|
{
|
|
return this._inject(html, function(frag, node)
|
|
{
|
|
var docFrag = document.createDocumentFragment();
|
|
var elms = (typeof frag === 'string' || typeof frag === 'number') ? this.create(frag) : (frag instanceof Node) ? [frag] : this._toArray(frag);
|
|
|
|
for (var i = 0; i < elms.length; i++)
|
|
{
|
|
docFrag.appendChild(elms[i]);
|
|
}
|
|
|
|
var result = docFrag.childNodes[0];
|
|
node.parentNode.replaceChild(docFrag, node);
|
|
|
|
return result;
|
|
|
|
});
|
|
},
|
|
remove: function()
|
|
{
|
|
return this.each(function(node)
|
|
{
|
|
if (node.parentNode) node.parentNode.removeChild(node);
|
|
});
|
|
},
|
|
clone: function(events)
|
|
{
|
|
var nodes = [];
|
|
this.each(function(node)
|
|
{
|
|
var copy = this._clone(node);
|
|
if (events) copy = this._cloneEvents(node, copy);
|
|
nodes.push(copy);
|
|
});
|
|
|
|
return new Dom(nodes);
|
|
},
|
|
|
|
// show/hide
|
|
show: function()
|
|
{
|
|
return this.each(function(node)
|
|
{
|
|
if (!node.style || !this._hasDisplayNone(node)) return;
|
|
|
|
var target = node.getAttribute('domTargetShow');
|
|
var isHidden = (node.classList) ? node.classList.contains(DomHClass) : false;
|
|
var isHiddenMobile = (node.classList) ? node.classList.contains(DomHMClass) : false;
|
|
var type;
|
|
|
|
if (isHidden)
|
|
{
|
|
type = DomHClass;
|
|
node.classList.remove(DomHClass);
|
|
}
|
|
else if (isHiddenMobile)
|
|
{
|
|
type = DomHMClass;
|
|
node.classList.remove(DomHMClass);
|
|
}
|
|
else
|
|
{
|
|
node.style.display = (target) ? target : 'block';
|
|
}
|
|
|
|
if (type) node.setAttribute('domTargetHide', type);
|
|
node.removeAttribute('domTargetShow');
|
|
|
|
}.bind(this));
|
|
},
|
|
hide: function()
|
|
{
|
|
return this.each(function(node)
|
|
{
|
|
if (!node.style || this._hasDisplayNone(node)) return;
|
|
|
|
var display = node.style.display;
|
|
var target = node.getAttribute('domTargetHide');
|
|
|
|
if (target === DomHClass)
|
|
{
|
|
node.classList.add(DomHClass);
|
|
}
|
|
else if (target === DomHMClass)
|
|
{
|
|
node.classList.add(DomHMClass);
|
|
}
|
|
else
|
|
{
|
|
if (display !== 'block') node.setAttribute('domTargetShow', display);
|
|
node.style.display = 'none';
|
|
}
|
|
|
|
node.removeAttribute('domTargetHide');
|
|
|
|
});
|
|
},
|
|
|
|
// dimensions
|
|
scrollTop: function(value)
|
|
{
|
|
var node = this.get();
|
|
var isWindow = (node === window);
|
|
var isDocument = (node.nodeType === 9);
|
|
var el = (isDocument) ? (document.scrollingElement || document.body.parentNode || document.body || document.documentElement) : node;
|
|
|
|
if (value !== undefined)
|
|
{
|
|
if (isWindow) window.scrollTo(0, value);
|
|
else el.scrollTop = value;
|
|
return;
|
|
}
|
|
|
|
if (isDocument)
|
|
{
|
|
return (typeof window.pageYOffset != 'undefined') ? window.pageYOffset : ((document.documentElement.scrollTop) ? document.documentElement.scrollTop : ((document.body.scrollTop) ? document.body.scrollTop : 0));
|
|
}
|
|
else
|
|
{
|
|
return (isWindow) ? window.pageYOffset : el.scrollTop;
|
|
}
|
|
},
|
|
offset: function()
|
|
{
|
|
return this._getDim('Offset');
|
|
},
|
|
position: function()
|
|
{
|
|
return this._getDim('Position');
|
|
},
|
|
width: function(value, adjust)
|
|
{
|
|
return this._getSize('width', 'Width', value, adjust);
|
|
},
|
|
height: function(value, adjust)
|
|
{
|
|
return this._getSize('height', 'Height', value, adjust);
|
|
},
|
|
outerWidth: function()
|
|
{
|
|
return this._getInnerOrOuter('width', 'outer');
|
|
},
|
|
outerHeight: function()
|
|
{
|
|
return this._getInnerOrOuter('height', 'outer');
|
|
},
|
|
innerWidth: function()
|
|
{
|
|
return this._getInnerOrOuter('width', 'inner');
|
|
},
|
|
innerHeight: function()
|
|
{
|
|
return this._getInnerOrOuter('height', 'inner');
|
|
},
|
|
|
|
// events
|
|
click: function()
|
|
{
|
|
return this._triggerEvent('click');
|
|
},
|
|
focus: function()
|
|
{
|
|
return this._triggerEvent('focus');
|
|
},
|
|
trigger: function(names)
|
|
{
|
|
return this.each(function(node)
|
|
{
|
|
var events = names.split(' ');
|
|
for (var i = 0; i < events.length; i++)
|
|
{
|
|
var ev;
|
|
var opts = { bubbles: true, cancelable: true };
|
|
|
|
try {
|
|
ev = new window.CustomEvent(events[i], opts);
|
|
} catch(e) {
|
|
ev = document.createEvent('CustomEvent');
|
|
ev.initCustomEvent(events[i], true, true);
|
|
}
|
|
|
|
node.dispatchEvent(ev);
|
|
}
|
|
});
|
|
},
|
|
on: function(names, handler, one)
|
|
{
|
|
return this.each(function(node)
|
|
{
|
|
var events = names.split(' ');
|
|
for (var i = 0; i < events.length; i++)
|
|
{
|
|
var event = this._getEventName(events[i]);
|
|
var namespace = this._getEventNamespace(events[i]);
|
|
|
|
handler = (one) ? this._getOneHandler(handler, names) : handler;
|
|
node.addEventListener(event, handler);
|
|
|
|
node._e = node._e || {};
|
|
node._e[namespace] = node._e[namespace] || {};
|
|
node._e[namespace][event] = node._e[namespace][event] || [];
|
|
node._e[namespace][event].push(handler);
|
|
}
|
|
|
|
});
|
|
},
|
|
one: function(events, handler)
|
|
{
|
|
return this.on(events, handler, true);
|
|
},
|
|
off: function(names, handler)
|
|
{
|
|
var testEvent = function(name, key, event) { return (name === event); };
|
|
var testNamespace = function(name, key, event, namespace) { return (key === namespace); };
|
|
var testEventNamespace = function(name, key, event, namespace) { return (name === event && key === namespace); };
|
|
var testPositive = function() { return true; };
|
|
|
|
if (names === undefined)
|
|
{
|
|
// ALL
|
|
return this.each(function(node)
|
|
{
|
|
this._offEvent(node, false, false, handler, testPositive);
|
|
});
|
|
}
|
|
|
|
return this.each(function(node)
|
|
{
|
|
var events = names.split(' ');
|
|
|
|
for (var i = 0; i < events.length; i++)
|
|
{
|
|
var event = this._getEventName(events[i]);
|
|
var namespace = this._getEventNamespace(events[i]);
|
|
|
|
// 1) event without namespace
|
|
if (namespace === '_events') this._offEvent(node, event, namespace, handler, testEvent);
|
|
// 2) only namespace
|
|
else if (!event && namespace !== '_events') this._offEvent(node, event, namespace, handler, testNamespace);
|
|
// 3) event + namespace
|
|
else this._offEvent(node, event, namespace, handler, testEventNamespace);
|
|
}
|
|
});
|
|
},
|
|
|
|
// form
|
|
serialize: function(asObject)
|
|
{
|
|
var obj = {};
|
|
var elms = this.get().elements;
|
|
for (var i = 0; i < elms.length; i++)
|
|
{
|
|
var el = elms[i];
|
|
if (/(checkbox|radio)/.test(el.type) && !el.checked) continue;
|
|
if (!el.name || el.disabled || el.type === 'file') continue;
|
|
|
|
if (el.type === 'select-multiple')
|
|
{
|
|
for (var z = 0; z < el.options.length; z++)
|
|
{
|
|
var opt = el.options[z];
|
|
if (opt.selected) obj[el.name] = opt.value;
|
|
}
|
|
}
|
|
|
|
obj[el.name] = (this._isNumber(el.value)) ? parseFloat(el.value) : this._getBooleanFromStr(el.value);
|
|
}
|
|
|
|
return (asObject) ? obj : this._toParams(obj);
|
|
},
|
|
ajax: function(success, error)
|
|
{
|
|
if (typeof AjaxRequest !== 'undefined')
|
|
{
|
|
var method = this.attr('method') || 'post';
|
|
var options = {
|
|
url: this.attr('action'),
|
|
data: this.serialize(),
|
|
success: success,
|
|
error: error
|
|
};
|
|
|
|
return new AjaxRequest(method, options);
|
|
}
|
|
},
|
|
|
|
// private
|
|
_queryContext: function(selector, context)
|
|
{
|
|
context = this._getContext(context);
|
|
|
|
return (context.nodeType !== 3 && typeof context.querySelectorAll === 'function') ? context.querySelectorAll(selector) : [];
|
|
},
|
|
_query: function(selector, context)
|
|
{
|
|
if (context)
|
|
{
|
|
return this._queryContext(selector, context);
|
|
}
|
|
else if (/^[.#]?[\w-]*$/.test(selector))
|
|
{
|
|
if (selector[0] === '#')
|
|
{
|
|
var element = document.getElementById(selector.slice(1));
|
|
return element ? [element] : [];
|
|
}
|
|
|
|
if (selector[0] === '.')
|
|
{
|
|
return document.getElementsByClassName(selector.slice(1));
|
|
}
|
|
|
|
return document.getElementsByTagName(selector);
|
|
}
|
|
|
|
return document.querySelectorAll(selector);
|
|
},
|
|
_getContext: function(context)
|
|
{
|
|
context = (typeof context === 'string') ? document.querySelector(context) : context;
|
|
|
|
return (context && context.dom) ? context.get() : (context || document);
|
|
},
|
|
_inject: function(html, fn)
|
|
{
|
|
var len = this.nodes.length;
|
|
var nodes = [];
|
|
while (len--)
|
|
{
|
|
var res = (typeof html === 'function') ? html.call(this, this.nodes[len]) : html;
|
|
var el = (len === 0) ? res : this._clone(res);
|
|
var node = fn.call(this, el, this.nodes[len]);
|
|
|
|
if (node)
|
|
{
|
|
if (node.dom) nodes.push(node.get());
|
|
else nodes.push(node);
|
|
}
|
|
}
|
|
|
|
return new Dom(nodes);
|
|
},
|
|
_cloneEvents: function(node, copy)
|
|
{
|
|
var events = node._e;
|
|
if (events)
|
|
{
|
|
copy._e = events;
|
|
for (var name in events._events)
|
|
{
|
|
for (var i = 0; i < events._events[name].length; i++)
|
|
{
|
|
copy.addEventListener(name, events._events[name][i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return copy;
|
|
},
|
|
_clone: function(node)
|
|
{
|
|
if (typeof node === 'undefined') return;
|
|
if (typeof node === 'string') return node;
|
|
else if (node instanceof Node || node.nodeType) return node.cloneNode(true);
|
|
else if ('length' in node)
|
|
{
|
|
return [].map.call(this._toArray(node), function(el) { return el.cloneNode(true); });
|
|
}
|
|
},
|
|
_slice: function(obj)
|
|
{
|
|
return (!obj || obj.length === 0) ? [] : (obj.length) ? [].slice.call(obj.nodes || obj) : [obj];
|
|
},
|
|
_eachClass: function(value, type)
|
|
{
|
|
return this.each(function(node)
|
|
{
|
|
if (value)
|
|
{
|
|
var setClass = function(name) { if (node.classList) node.classList[type](name); };
|
|
value.split(' ').forEach(setClass);
|
|
}
|
|
});
|
|
},
|
|
_triggerEvent: function(name)
|
|
{
|
|
var node = this.get();
|
|
if (node && node.nodeType !== 3) node[name]();
|
|
return this;
|
|
},
|
|
_getOneHandler: function(handler, events)
|
|
{
|
|
var self = this;
|
|
return function()
|
|
{
|
|
handler.apply(this, arguments);
|
|
self.off(events);
|
|
};
|
|
},
|
|
_getEventNamespace: function(event)
|
|
{
|
|
var arr = event.split('.');
|
|
var namespace = (arr[1]) ? arr[1] : '_events';
|
|
return (arr[2]) ? namespace + arr[2] : namespace;
|
|
},
|
|
_getEventName: function(event)
|
|
{
|
|
return event.split('.')[0];
|
|
},
|
|
_offEvent: function(node, event, namespace, handler, condition)
|
|
{
|
|
for (var key in node._e)
|
|
{
|
|
for (var name in node._e[key])
|
|
{
|
|
if (condition(name, key, event, namespace))
|
|
{
|
|
var handlers = node._e[key][name];
|
|
for (var i = 0; i < handlers.length; i++)
|
|
{
|
|
if (typeof handler !== 'undefined' && handlers[i].toString() !== handler.toString())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
node.removeEventListener(name, handlers[i]);
|
|
node._e[key][name].splice(i, 1);
|
|
|
|
if (node._e[key][name].length === 0) delete node._e[key][name];
|
|
if (Object.keys(node._e[key]).length === 0) delete node._e[key];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_getInnerOrOuter: function(method, type)
|
|
{
|
|
return this[method](undefined, type);
|
|
},
|
|
_getDocSize: function(node, type)
|
|
{
|
|
var body = node.body, html = node.documentElement;
|
|
return Math.max(body['scroll' + type], body['offset' + type], html['client' + type], html['scroll' + type], html['offset' + type]);
|
|
},
|
|
_getSize: function(type, captype, value, adjust)
|
|
{
|
|
if (value === undefined)
|
|
{
|
|
var el = this.get();
|
|
if (el.nodeType === 3) value = 0;
|
|
else if (el.nodeType === 9) value = this._getDocSize(el, captype);
|
|
else if (el === window) value = window['inner' + captype];
|
|
else value = this._getHeightOrWidth(type, el, adjust || 'normal');
|
|
|
|
return Math.round(value);
|
|
}
|
|
|
|
return this.each(function(node)
|
|
{
|
|
value = parseFloat(value);
|
|
value = value + this._adjustResultHeightOrWidth(type, node, adjust || 'normal');
|
|
|
|
new Dom(node).css(type, value + 'px');
|
|
|
|
}.bind(this));
|
|
},
|
|
_getHeightOrWidth: function(type, el, adjust)
|
|
{
|
|
if (!el) return 0;
|
|
|
|
var name = type.charAt(0).toUpperCase() + type.slice(1);
|
|
var result = 0;
|
|
var style = getComputedStyle(el, null);
|
|
var $el = new Dom(el);
|
|
var $targets = $el.parents().filter(function(node)
|
|
{
|
|
return (node.nodeType === 1 && getComputedStyle(node, null).display === 'none') ? node : false;
|
|
});
|
|
|
|
if (style.display === 'none') $targets.add(el);
|
|
if ($targets.length !== 0)
|
|
{
|
|
var fixStyle = 'visibility: hidden !important; display: block !important;';
|
|
var tmp = [];
|
|
|
|
$targets.each(function(node)
|
|
{
|
|
var $node = new Dom(node);
|
|
var thisStyle = $node.attr('style');
|
|
if (thisStyle !== null) tmp.push(thisStyle);
|
|
$node.attr('style', (thisStyle !== null) ? thisStyle + ';' + fixStyle : fixStyle);
|
|
});
|
|
|
|
result = $el.get()['offset' + name] - this._adjustResultHeightOrWidth(type, el, adjust);
|
|
|
|
$targets.each(function(node, i)
|
|
{
|
|
var $node = new Dom(node);
|
|
if (tmp[i] === undefined) $node.removeAttr('style');
|
|
else $node.attr('style', tmp[i]);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
result = el['offset' + name] - this._adjustResultHeightOrWidth(type, el, adjust);
|
|
}
|
|
|
|
return result;
|
|
},
|
|
_adjustResultHeightOrWidth: function(type, el, adjust)
|
|
{
|
|
if (!el || adjust === false) return 0;
|
|
|
|
var fix = 0;
|
|
var style = getComputedStyle(el, null);
|
|
var isBorderBox = (style.boxSizing === "border-box");
|
|
|
|
if (type === 'height')
|
|
{
|
|
if (adjust === 'inner' || (adjust === 'normal' && isBorderBox))
|
|
{
|
|
fix += (parseFloat(style.borderTopWidth) || 0) + (parseFloat(style.borderBottomWidth) || 0);
|
|
}
|
|
|
|
if (adjust === 'outer') fix -= (parseFloat(style.marginTop) || 0) + (parseFloat(style.marginBottom) || 0);
|
|
}
|
|
else
|
|
{
|
|
if (adjust === 'inner' || (adjust === 'normal' && isBorderBox))
|
|
{
|
|
fix += (parseFloat(style.borderLeftWidth) || 0) + (parseFloat(style.borderRightWidth) || 0);
|
|
}
|
|
|
|
if (adjust === 'outer') fix -= (parseFloat(style.marginLeft) || 0) + (parseFloat(style.marginRight) || 0);
|
|
}
|
|
|
|
return fix;
|
|
},
|
|
_getDim: function(type)
|
|
{
|
|
var node = this.get();
|
|
return (node.nodeType === 3) ? { top: 0, left: 0 } : this['_get' + type](node);
|
|
},
|
|
_getPosition: function(node)
|
|
{
|
|
return { top: node.offsetTop, left: node.offsetLeft };
|
|
},
|
|
_getOffset: function(node)
|
|
{
|
|
var rect = node.getBoundingClientRect();
|
|
var doc = node.ownerDocument;
|
|
var docElem = doc.documentElement;
|
|
var win = doc.defaultView;
|
|
|
|
return {
|
|
top: rect.top + win.pageYOffset - docElem.clientTop,
|
|
left: rect.left + win.pageXOffset - docElem.clientLeft
|
|
};
|
|
},
|
|
_getSibling: function(selector, method)
|
|
{
|
|
selector = (selector && selector.dom) ? selector.get() : selector;
|
|
|
|
var isNode = (selector && selector.nodeType);
|
|
var sibling;
|
|
|
|
this.each(function(node)
|
|
{
|
|
while (node = node[method])
|
|
{
|
|
if ((isNode && node === selector) || new Dom(node).is(selector))
|
|
{
|
|
sibling = node;
|
|
return;
|
|
}
|
|
}
|
|
});
|
|
|
|
return new Dom(sibling);
|
|
},
|
|
_toArray: function(obj)
|
|
{
|
|
if (obj instanceof NodeList)
|
|
{
|
|
var arr = [];
|
|
for (var i = 0; i < obj.length; i++)
|
|
{
|
|
arr[i] = obj[i];
|
|
}
|
|
|
|
return arr;
|
|
}
|
|
else if (obj === undefined) return [];
|
|
else
|
|
{
|
|
return (obj.dom) ? obj.nodes : obj;
|
|
}
|
|
},
|
|
_toParams: function(obj)
|
|
{
|
|
var params = '';
|
|
for (var key in obj)
|
|
{
|
|
params += '&' + this._encodeUri(key) + '=' + this._encodeUri(obj[key]);
|
|
}
|
|
|
|
return params.replace(/^&/, '');
|
|
},
|
|
_toObject: function(str)
|
|
{
|
|
return (new Function("return " + str))();
|
|
},
|
|
_encodeUri: function(str)
|
|
{
|
|
return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+');
|
|
},
|
|
_isNumber: function(str)
|
|
{
|
|
return !isNaN(str) && !isNaN(parseFloat(str));
|
|
},
|
|
_isObjectString: function(str)
|
|
{
|
|
return (str.search(/^{/) !== -1);
|
|
},
|
|
_getBooleanFromStr: function(str)
|
|
{
|
|
if (str === 'true') return true;
|
|
else if (str === 'false') return false;
|
|
|
|
return str;
|
|
},
|
|
_hasDisplayNone: function(el)
|
|
{
|
|
return (el.style.display === 'none') || ((el.currentStyle) ? el.currentStyle.display : getComputedStyle(el, null).display) === 'none';
|
|
}
|
|
};
|
|
// Wrapper
|
|
var $K = {};
|
|
|
|
// Globals
|
|
$K.app = [];
|
|
$K.version = '7.2.1';
|
|
$K.options = {};
|
|
$K.modules = {};
|
|
$K.services = {};
|
|
$K.plugins = {};
|
|
$K.classes = {};
|
|
$K.extends = {};
|
|
$K.lang = {};
|
|
$K.dom = function(selector, context) { return new Dom(selector, context); };
|
|
$K.ajax = Ajax;
|
|
$K.Dom = Dom;
|
|
$K.env = {
|
|
'module': 'modules',
|
|
'service': 'services',
|
|
'plugin': 'plugins',
|
|
'class': 'classes',
|
|
'extend': 'extends'
|
|
};
|
|
|
|
// init class
|
|
var KubeApp = function(options, args)
|
|
{
|
|
return ($K.app = new App(options));
|
|
};
|
|
|
|
// init
|
|
$K.init = function(options)
|
|
{
|
|
return new KubeApp(options, [].slice.call(arguments, 1));
|
|
};
|
|
|
|
// api
|
|
$K.api = function(name)
|
|
{
|
|
var app = $K.app;
|
|
var args = [].slice.call(arguments, 1);
|
|
|
|
if (app)
|
|
{
|
|
args.unshift(name);
|
|
app.api.apply(app, args);
|
|
}
|
|
};
|
|
|
|
// add
|
|
$K.add = function(type, name, obj)
|
|
{
|
|
if (typeof $K.env[type] === 'undefined') return;
|
|
|
|
// translations
|
|
if (obj.translations)
|
|
{
|
|
$K.lang = $K.extend(true, {}, $K.lang, obj.translations);
|
|
}
|
|
|
|
// extend
|
|
if (type === 'extend')
|
|
{
|
|
$K[$K.env[type]][name] = obj;
|
|
}
|
|
else
|
|
{
|
|
// prototype
|
|
var F = function() {};
|
|
F.prototype = obj;
|
|
|
|
// extends
|
|
if (obj.extends)
|
|
{
|
|
for (var i = 0; i < obj.extends.length; i++)
|
|
{
|
|
$K.inherit(F, $K.extends[obj.extends[i]]);
|
|
}
|
|
}
|
|
|
|
$K[$K.env[type]][name] = F;
|
|
}
|
|
};
|
|
|
|
// add lang
|
|
$K.addLang = function(lang, obj)
|
|
{
|
|
if (typeof $K.lang[lang] === 'undefined')
|
|
{
|
|
$K.lang[lang] = {};
|
|
}
|
|
|
|
$K.lang[lang] = $K.extend($K.lang[lang], obj);
|
|
};
|
|
|
|
// create
|
|
$K.create = function(name)
|
|
{
|
|
var arr = name.split('.');
|
|
var args = [].slice.call(arguments, 1);
|
|
|
|
var type = 'classes';
|
|
if (typeof $K.env[arr[0]] !== 'undefined')
|
|
{
|
|
type = $K.env[arr[0]];
|
|
name = arr.slice(1).join('.');
|
|
}
|
|
|
|
// construct
|
|
var instance = new $K[type][name]();
|
|
|
|
instance._type = arr[0];
|
|
instance._name = name;
|
|
|
|
// init
|
|
if (instance.init)
|
|
{
|
|
var res = instance.init.apply(instance, args);
|
|
|
|
return (res) ? res : instance;
|
|
}
|
|
|
|
return instance;
|
|
};
|
|
|
|
// inherit
|
|
$K.inherit = function(current, parent)
|
|
{
|
|
var F = function () {};
|
|
F.prototype = parent;
|
|
var f = new F();
|
|
|
|
for (var prop in current.prototype)
|
|
{
|
|
if (current.prototype.__lookupGetter__(prop)) f.__defineGetter__(prop, current.prototype.__lookupGetter__(prop));
|
|
else f[prop] = current.prototype[prop];
|
|
}
|
|
|
|
current.prototype = f;
|
|
current.prototype.super = parent;
|
|
|
|
return current;
|
|
};
|
|
|
|
// error
|
|
$K.error = function(exception)
|
|
{
|
|
throw exception;
|
|
};
|
|
|
|
// extend
|
|
$K.extend = function()
|
|
{
|
|
var extended = {};
|
|
var deep = false;
|
|
var i = 0;
|
|
var length = arguments.length;
|
|
|
|
if (Object.prototype.toString.call( arguments[0] ) === '[object Boolean]')
|
|
{
|
|
deep = arguments[0];
|
|
i++;
|
|
}
|
|
|
|
var merge = function(obj)
|
|
{
|
|
for (var prop in obj)
|
|
{
|
|
if (Object.prototype.hasOwnProperty.call(obj, prop))
|
|
{
|
|
if (deep && Object.prototype.toString.call(obj[prop]) === '[object Object]') extended[prop] = $K.extend(true, extended[prop], obj[prop]);
|
|
else extended[prop] = obj[prop];
|
|
}
|
|
}
|
|
};
|
|
|
|
for (; i < length; i++ )
|
|
{
|
|
var obj = arguments[i];
|
|
merge(obj);
|
|
}
|
|
|
|
return extended;
|
|
};
|
|
var App = function(options)
|
|
{
|
|
this.modules = {};
|
|
this.services = [];
|
|
this.queueStart = { 'service': {}, 'module': {} };
|
|
this.queueStop = { 'service': {}, 'module': {} };
|
|
this.started = false;
|
|
this.stopped = false;
|
|
|
|
// environment
|
|
this.namespace = 'kube';
|
|
this.dataNamespace = 'data-kube';
|
|
this.instancePrefix = 'kube-instance-';
|
|
this.rootOpts = options;
|
|
this.$win = $K.dom(window);
|
|
this.$doc = $K.dom(document);
|
|
this.$body = $K.dom('body');
|
|
|
|
// core services
|
|
this.coreServices = ['options', 'lang', 'utils'];
|
|
this.bindableServices = ['opts', 'lang', 'utils', '$win', '$doc', '$body']
|
|
|
|
this.utils = $K.create('service.utils', this);
|
|
this.opts = $K.create('service.options', this, 'global', options);
|
|
this.lang = $K.create('service.lang', this);
|
|
|
|
this.appcallback = new App.Callback(this);
|
|
this.appstarter = new App.Starter(this);
|
|
this.appbuilder = new App.Builder(this);
|
|
this.appbroadcast = new App.Broadcast(this);
|
|
this.appapi = new App.Api(this);
|
|
|
|
this.build();
|
|
this.start();
|
|
};
|
|
|
|
App.prototype = {
|
|
|
|
// build
|
|
build: function()
|
|
{
|
|
this.appbuilder.build();
|
|
},
|
|
|
|
// start & stop
|
|
start: function()
|
|
{
|
|
// start
|
|
this.stopped = false;
|
|
this.broadcast('start', this);
|
|
|
|
// starter
|
|
this.appstarter.start();
|
|
|
|
// started
|
|
this.broadcast('started', this);
|
|
this.started = true;
|
|
},
|
|
stop: function()
|
|
{
|
|
this.started = false;
|
|
this.stopped = true;
|
|
|
|
// stop
|
|
this.broadcast('stop', this);
|
|
|
|
// stopper
|
|
this.appstarter.stop();
|
|
|
|
// stopped
|
|
this.broadcast('stopped', this);
|
|
},
|
|
|
|
// starter & stopper
|
|
starter: function(instance, priority)
|
|
{
|
|
var type = (instance._type !== 'service') ? 'module' : instance._type;
|
|
this.queueStart[type][priority] = instance._name;
|
|
},
|
|
stopper: function(instance, priority)
|
|
{
|
|
var type = (instance._type !== 'service') ? 'module' : instance._type;
|
|
this.queueStop[type][priority] = instance._name;
|
|
},
|
|
|
|
// started & stopped
|
|
isStarted: function()
|
|
{
|
|
return this.started;
|
|
},
|
|
isStopped: function()
|
|
{
|
|
return this.stopped;
|
|
},
|
|
|
|
// broadcast
|
|
broadcast: function(name, sender)
|
|
{
|
|
this.appbroadcast.trigger(name, sender, [].slice.call(arguments, 2));
|
|
},
|
|
|
|
// callback
|
|
on: function(name, func)
|
|
{
|
|
this.appcallback.add(name, func);
|
|
},
|
|
off: function(name, func)
|
|
{
|
|
this.appcallback.remove(name, func);
|
|
},
|
|
|
|
// api
|
|
api: function(name)
|
|
{
|
|
return this.appapi.trigger(name, [].slice.call(arguments, 1));
|
|
}
|
|
};
|
|
App.Module = function(app, $el, name, id)
|
|
{
|
|
this.app = app;
|
|
this.instancePrefix = app.instancePrefix;
|
|
|
|
// local
|
|
this.eventTypes = ['click', 'mouseover', 'mouseout', 'mousedown', 'mouseup', 'mousemove',
|
|
'keydown', 'keyup', 'focus', 'submit', 'change', 'contextmenu', 'input'];
|
|
|
|
// build
|
|
return this._build($el, name, id);
|
|
};
|
|
|
|
App.Module.prototype = {
|
|
_build: function($el, name, id)
|
|
{
|
|
var instance = $el.dataget(this.instancePrefix + name);
|
|
if (!instance && typeof $K.modules[name] !== 'undefined')
|
|
{
|
|
var context = new App.Context(this.app, $el, id);
|
|
var $target = context.getTarget();
|
|
|
|
instance = $K.create('module.' + name, this.app, context);
|
|
instance._id = id;
|
|
|
|
$el.dataset(this.instancePrefix + name, instance);
|
|
$el.attr('data-loaded', true);
|
|
|
|
// delegate events
|
|
this._delegateModuleEvents(instance, $el, name);
|
|
|
|
// delegate commands
|
|
this._delegateModuleCommands(instance, $el);
|
|
|
|
if ($target.is())
|
|
{
|
|
this._delegateModuleCommands(instance, $target);
|
|
}
|
|
}
|
|
|
|
return instance;
|
|
},
|
|
|
|
_delegateModuleCommands: function(instance, $el)
|
|
{
|
|
$el.find('[data-command]').each(function(node)
|
|
{
|
|
this._delegateCommand(instance, node, node.getAttribute('data-command'));
|
|
|
|
}.bind(this));
|
|
},
|
|
_delegateCommand: function(instance, node, command)
|
|
{
|
|
if (typeof instance._eventCommands === 'undefined') instance._eventCommands = [];
|
|
|
|
var self = this;
|
|
var $node = $K.dom(node);
|
|
|
|
instance._eventCommands.push($node);
|
|
|
|
$node.on('click.generatedcommand', function(e)
|
|
{
|
|
e.preventDefault();
|
|
|
|
var args = $node.data();
|
|
args.event = e;
|
|
|
|
self.app.broadcast(command, instance, $node, args);
|
|
});
|
|
},
|
|
_delegateModuleEvents: function(instance, $el, name)
|
|
{
|
|
$el.find('[data-type]').each(function(node)
|
|
{
|
|
var arr = node.getAttribute('data-type').split('.');
|
|
var type = arr[0];
|
|
var scope = name;
|
|
|
|
if (arr.length === 2)
|
|
{
|
|
scope = arr[0];
|
|
type = arr[1];
|
|
}
|
|
|
|
if (scope === name)
|
|
{
|
|
this._delegateEvent(instance, name, node, type);
|
|
}
|
|
|
|
}.bind(this));
|
|
},
|
|
_delegateEvent: function(instance, name, node, type)
|
|
{
|
|
if (typeof instance._eventNodes === 'undefined') instance._eventNodes = [];
|
|
|
|
var $node = $K.dom(node);
|
|
var callback = function(e, eventType, element, type, args)
|
|
{
|
|
return instance['on' + eventType].call(instance, e, element, type, args);
|
|
};
|
|
|
|
instance._eventNodes.push($node);
|
|
|
|
for (var i = 0; i < this.eventTypes.length; i++)
|
|
{
|
|
var event = 'on' + this.eventTypes[i];
|
|
if (typeof instance[event] === 'function')
|
|
{
|
|
$node.on(this.eventTypes[i] + '.generatedevent', function(e)
|
|
{
|
|
var args = $node.data();
|
|
callback(e, e.type, this, type, args);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
};
|
|
App.Context = function(app, $el, name)
|
|
{
|
|
this.app = app;
|
|
this.opts = app.opts;
|
|
|
|
// build
|
|
this.$element = this._buildElement($el);
|
|
this.params = this._buildParams();
|
|
this.name = this._buildName(name);
|
|
this.$target = this._buildTarget();
|
|
};
|
|
|
|
App.Context.prototype = {
|
|
|
|
// public
|
|
getElement: function()
|
|
{
|
|
return this.$element;
|
|
},
|
|
getTarget: function()
|
|
{
|
|
return this.$target;
|
|
},
|
|
getParams: function(defaults)
|
|
{
|
|
return (defaults) ? $K.extend({}, defaults, this.params) : this.params;
|
|
},
|
|
getName: function()
|
|
{
|
|
return this.name;
|
|
},
|
|
|
|
// private
|
|
_buildName: function(name)
|
|
{
|
|
return (this.params.name) ? this.params.name : name;
|
|
},
|
|
_buildParams: function()
|
|
{
|
|
return $K.create('service.options', this.app, 'element', this.$element);
|
|
},
|
|
_buildElement: function($el)
|
|
{
|
|
return new App.Element(this.app, $el);
|
|
},
|
|
_buildTarget: function()
|
|
{
|
|
return new App.Target(this.app, this.params.target);
|
|
}
|
|
};
|
|
App.Callback = function(app)
|
|
{
|
|
this.app = app;
|
|
this.opts = app.opts;
|
|
|
|
// local
|
|
this.callbacks = {};
|
|
|
|
// build
|
|
this._build();
|
|
};
|
|
|
|
App.Callback.prototype = {
|
|
stop: function()
|
|
{
|
|
this.callbacks = {};
|
|
},
|
|
add: function(name, handler)
|
|
{
|
|
if (typeof this.callbacks[name] === 'undefined') this.callbacks[name] = [];
|
|
|
|
this.callbacks[name].push(handler);
|
|
},
|
|
remove: function(name, handler)
|
|
{
|
|
if (handler === undefined)
|
|
{
|
|
delete this.callbacks[name];
|
|
}
|
|
else
|
|
{
|
|
for (var i = 0; i < this.callbacks[name].length; i++)
|
|
{
|
|
this.callbacks[name].splice(i, 1);
|
|
}
|
|
|
|
if (this.callbacks[name].length === 0)
|
|
{
|
|
delete this.callbacks[name];
|
|
}
|
|
}
|
|
},
|
|
trigger: function(name, args)
|
|
{
|
|
if (typeof this.callbacks[name] === 'undefined') return;
|
|
|
|
for (var i = 0; i < this.callbacks[name].length; i++)
|
|
{
|
|
this.callbacks[name][i].apply(this.app, args);
|
|
}
|
|
},
|
|
|
|
// private
|
|
_build: function()
|
|
{
|
|
if (this.opts.callbacks)
|
|
{
|
|
for (var name in this.opts.callbacks)
|
|
{
|
|
if (typeof this.opts.callbacks[name] === 'function')
|
|
{
|
|
if (typeof this.callbacks[name] === 'undefined') this.callbacks[name] = [];
|
|
this.callbacks[name].push(this.opts.callbacks[name]);
|
|
}
|
|
else
|
|
{
|
|
for (var key in this.opts.callbacks[name])
|
|
{
|
|
if (typeof this.callbacks[name + '.' + key] === 'undefined') this.callbacks[name + '.' + key] = [];
|
|
this.callbacks[name + '.' + key].push(this.opts.callbacks[name][key]);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
App.Element = function(app, $el)
|
|
{
|
|
this.app = app;
|
|
this.parse($el);
|
|
};
|
|
|
|
App.Element.prototype = {
|
|
isOpened: function()
|
|
{
|
|
return !this.isClosed();
|
|
},
|
|
isClosed: function()
|
|
{
|
|
return (this.hasClass('is-hidden') || this.css('display') === 'none');
|
|
}
|
|
};
|
|
|
|
$K.inherit(App.Element, Dom.prototype);
|
|
App.Target = function(app, selector)
|
|
{
|
|
this.app = app;
|
|
this.parse(selector);
|
|
};
|
|
|
|
App.Target.prototype = {
|
|
isOpened: function()
|
|
{
|
|
return !this.isClosed();
|
|
},
|
|
isClosed: function()
|
|
{
|
|
var self = this;
|
|
var count = 0;
|
|
var len = this.length;
|
|
this.each(function(node)
|
|
{
|
|
var $node = $K.dom(node);
|
|
if ($node.hasClass('is-hidden') || $node.css('display') === 'none')
|
|
{
|
|
count++;
|
|
}
|
|
});
|
|
|
|
return (count === len);
|
|
}
|
|
};
|
|
|
|
$K.inherit(App.Target, Dom.prototype);
|
|
App.Api = function(app)
|
|
{
|
|
this.app = app;
|
|
this.modules = app.modules;
|
|
};
|
|
|
|
App.Api.prototype = {
|
|
trigger: function(name, args)
|
|
{
|
|
var arr = name.split('.');
|
|
var isNamed = (arr.length === 3);
|
|
var isApp = (arr.length === 1);
|
|
var isCallback = (arr[0] === 'on' || arr[0] === 'off');
|
|
|
|
var module = arr[0];
|
|
var method = arr[1];
|
|
var id = false;
|
|
|
|
if (isApp)
|
|
{
|
|
module = false;
|
|
method = arr[0];
|
|
}
|
|
else if (isNamed)
|
|
{
|
|
method = arr[2];
|
|
id = arr[1];
|
|
}
|
|
|
|
// app
|
|
if (isApp)
|
|
{
|
|
if (typeof this.app[method] === 'function')
|
|
{
|
|
return this._call(this.app, method, args);
|
|
}
|
|
}
|
|
// callback
|
|
else if (isCallback)
|
|
{
|
|
return (module === 'on') ? this.app.on(module, args[0]) : this.app.off(module, args[0] || undefined);
|
|
}
|
|
else
|
|
{
|
|
// service
|
|
if (this._isInstanceExists(this.app, module))
|
|
{
|
|
return this._call(this.app[module], method, args);
|
|
}
|
|
// module / plugin / addon
|
|
else if (this._isInstanceExists(this.modules, module))
|
|
{
|
|
this._doApi(module, method, id, args)
|
|
}
|
|
}
|
|
},
|
|
|
|
// private
|
|
_isInstanceExists: function(obj, name)
|
|
{
|
|
return (typeof obj[name] !== 'undefined');
|
|
},
|
|
_doApi: function(module, method, id, args)
|
|
{
|
|
for (var key in this.modules[module])
|
|
{
|
|
if (id === false || id === key)
|
|
{
|
|
var instance = this.modules[module][key];
|
|
this._call(instance, method, args);
|
|
}
|
|
}
|
|
},
|
|
_call: function(instance, method, args)
|
|
{
|
|
if (typeof instance[method] === 'function')
|
|
{
|
|
return instance[method].apply(instance, args);
|
|
}
|
|
}
|
|
};
|
|
App.Broadcast = function(app)
|
|
{
|
|
this.app = app;
|
|
this.modules = app.modules;
|
|
this.callback = app.appcallback;
|
|
};
|
|
|
|
App.Broadcast.prototype = {
|
|
trigger: function(name, sender, args)
|
|
{
|
|
if (Array.isArray(name))
|
|
{
|
|
sender._id = name[0];
|
|
name = name[1];
|
|
}
|
|
else if (sender && typeof sender.context !== 'undefined')
|
|
{
|
|
sender._id = sender.context.getName();
|
|
}
|
|
|
|
args.unshift(sender);
|
|
|
|
for (var moduleName in this.modules)
|
|
{
|
|
for (var key in this.modules[moduleName])
|
|
{
|
|
var instance = this.modules[moduleName][key];
|
|
this._call(instance, name, args, sender);
|
|
}
|
|
}
|
|
|
|
this.callback.trigger(name, args);
|
|
},
|
|
|
|
|
|
// private
|
|
_call: function(instance, name, args, sender)
|
|
{
|
|
// new
|
|
if (typeof instance['onmessage'] !== 'undefined')
|
|
{
|
|
var arr = name.split('.');
|
|
var func = instance['onmessage'][arr[0]];
|
|
|
|
if (arr.length === 1 && typeof func === 'function')
|
|
{
|
|
func.apply(instance, args);
|
|
}
|
|
else if (arr.length === 2 && typeof func !== 'undefined' && typeof func[arr[1]] === 'function')
|
|
{
|
|
func[arr[1]].apply(instance, args);
|
|
}
|
|
}
|
|
|
|
// 7.1.1 compatibility
|
|
var arr = name.split('.');
|
|
if (arr.length === 1)
|
|
{
|
|
if (typeof instance['on' + name] === 'function')
|
|
{
|
|
instance['on' + name].apply(instance, args);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
arr[0] = 'on' + arr[0];
|
|
|
|
// without id
|
|
var func = this.app.utils.checkProperty(instance, arr);
|
|
if (typeof func === 'function')
|
|
{
|
|
func.apply(instance, args);
|
|
}
|
|
|
|
// with id
|
|
if (sender && sender._id)
|
|
{
|
|
var idArr = [arr[0], sender._id, arr[1]];
|
|
var func = this.app.utils.checkProperty(instance, idArr);
|
|
if (typeof func === 'function')
|
|
{
|
|
func.apply(instance, args);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
App.Builder = function(app)
|
|
{
|
|
this.app = app;
|
|
this.opts = app.opts;
|
|
this.$doc = app.$doc;
|
|
this.dataNamespace = app.dataNamespace;
|
|
};
|
|
|
|
App.Builder.prototype = {
|
|
build: function()
|
|
{
|
|
this._buildServices();
|
|
this._buildModules();
|
|
},
|
|
|
|
// private
|
|
_buildServices: function()
|
|
{
|
|
var services = [];
|
|
var startableServices = [];
|
|
for (var name in $K.services)
|
|
{
|
|
if (this.app.coreServices.indexOf(name) === -1)
|
|
{
|
|
this.app[name] = $K.create('service.' + name, this.app);
|
|
this.app.bindableServices.push(name);
|
|
services.push(name);
|
|
startableServices.push(name);
|
|
}
|
|
}
|
|
|
|
// make core services to use another services
|
|
for (var i = 0; i < this.app.coreServices.length; i++)
|
|
{
|
|
var name = this.app.coreServices[i];
|
|
if (name !== 'options') services.push(name);
|
|
}
|
|
|
|
// binding
|
|
for (var i = 0; i < services.length; i++)
|
|
{
|
|
var service = services[i];
|
|
for (var z = 0; z < this.app.bindableServices.length; z++)
|
|
{
|
|
var inj = this.app.bindableServices[z];
|
|
if (service !== inj)
|
|
{
|
|
this.app[service][inj] = this.app[inj];
|
|
}
|
|
}
|
|
}
|
|
|
|
this.app.services = startableServices;
|
|
},
|
|
_buildModules: function()
|
|
{
|
|
this.$doc.find('[' + this.dataNamespace + ']').each(function(node, i)
|
|
{
|
|
var $el = $K.dom(node);
|
|
var name = $el.attr(this.dataNamespace);
|
|
var id = ($el.attr('id')) ? $el.attr('id') : name + '-' + i;
|
|
id = ($el.attr('data-name')) ? $el.attr('data-name') : id;
|
|
var instance = new App.Module(this.app, $el, name, id);
|
|
|
|
this._storeElementModule(instance, name, id);
|
|
|
|
}.bind(this));
|
|
},
|
|
_storeElementModule: function(instance, name, id)
|
|
{
|
|
if (instance)
|
|
{
|
|
if (typeof this.app.modules[name] === 'undefined')
|
|
{
|
|
this.app.modules[name] = {};
|
|
}
|
|
|
|
this.app.modules[name][id] = instance;
|
|
}
|
|
}
|
|
};
|
|
App.Starter = function(app)
|
|
{
|
|
this.app = app;
|
|
this.queue = {
|
|
'start': app.queueStart,
|
|
'stop': app.queueStop
|
|
};
|
|
this.priority = {
|
|
'start': { 'service': [], 'module': [] },
|
|
'stop': { 'service': [], 'module': [] }
|
|
};
|
|
};
|
|
|
|
App.Starter.prototype = {
|
|
start: function()
|
|
{
|
|
this._stopStart('service', 'start');
|
|
this._stopStart('module', 'start');
|
|
},
|
|
stop: function()
|
|
{
|
|
this._stopStart('service', 'stop');
|
|
this._stopStart('module', 'stop');
|
|
},
|
|
|
|
// private
|
|
_stopStart: function(type, method)
|
|
{
|
|
// priority
|
|
var queue = this.queue[method][type];
|
|
for (var key in queue)
|
|
{
|
|
var name = queue[key];
|
|
var instance = (type === 'service') ? this.app[name] : this.app.modules[name];
|
|
|
|
this._callInstances(type, method, instance);
|
|
this.priority[method][type].push(name);
|
|
}
|
|
|
|
// common
|
|
var modules = (type === 'service') ? this.app.services : this.app.modules;
|
|
for (var key in modules)
|
|
{
|
|
var name = (type === 'service') ? modules[key] : key;
|
|
|
|
if (this.priority[method][type].indexOf(name) === -1)
|
|
{
|
|
var instance = (type === 'service') ? this.app[name] : modules[name];
|
|
this._callInstances(type, method, instance);
|
|
}
|
|
}
|
|
},
|
|
_stopModuleEvents: function(method, instance)
|
|
{
|
|
if (method === 'stop')
|
|
{
|
|
if (typeof instance._eventNodes !== 'undefined')
|
|
{
|
|
for (var i = 0; i < instance._eventNodes.length; i++)
|
|
{
|
|
instance._eventNodes[i].off('.generatedevent');
|
|
}
|
|
}
|
|
|
|
if (typeof instance._eventCommands !== 'undefined')
|
|
{
|
|
for (var i = 0; i < instance._eventCommands.length; i++)
|
|
{
|
|
instance._eventCommands[i].off('.generatedcommand');
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_callInstances: function(type, method, instance)
|
|
{
|
|
if (type === 'service')
|
|
{
|
|
this._call(instance, method);
|
|
}
|
|
else
|
|
{
|
|
for (var key in instance)
|
|
{
|
|
this._call(instance[key], method);
|
|
this._stopModuleEvents(method, instance[key]);
|
|
}
|
|
}
|
|
},
|
|
_call: function(instance, method, args)
|
|
{
|
|
if (typeof instance[method] === 'function')
|
|
{
|
|
return instance[method].apply(instance, args);
|
|
}
|
|
}
|
|
};
|
|
$K.add('extend', 'dom', $K.Dom.prototype);
|
|
$K.add('service', 'animate', {
|
|
init: function(app)
|
|
{
|
|
this.app = app;
|
|
|
|
// local
|
|
this.animationOpt = true;
|
|
},
|
|
run: function(element, animation, callback)
|
|
{
|
|
return new $K.AnimatePlay(this.app, element, animation, callback, this.animationOpt);
|
|
},
|
|
remove: function(element)
|
|
{
|
|
this.$el = $K.dom(element);
|
|
var effect = this.$el.attr('kube-animate-effect');
|
|
|
|
this.$el.hide();
|
|
this.$el.removeClass(effect);
|
|
this.$el.off('animationend webkitAnimationEnd');
|
|
}
|
|
});
|
|
|
|
$K.AnimatePlay = function(app, element, animation, callback, animationOpt)
|
|
{
|
|
this.hidableEffects = ['fadeOut', 'flipOut', 'slideUp', 'zoomOut', 'slideOutUp', 'slideOutRight', 'slideOutLeft'];
|
|
this.prefix = 'kube-';
|
|
this.prefixes = ['', '-webkit-'];
|
|
|
|
this.utils = app.utils;
|
|
this.$el = $K.dom(element);
|
|
this.$body = $K.dom('body');
|
|
this.callback = callback;
|
|
this.animation = (!animationOpt) ? this._buildAnimationOff(animation) : animation;
|
|
|
|
this._setHeight();
|
|
|
|
// animate
|
|
if (this._isAnimate()) this._animate();
|
|
else this._toggle();
|
|
};
|
|
|
|
$K.AnimatePlay.prototype = {
|
|
_setHeight: function()
|
|
{
|
|
if (this.animation === 'slideUp' || this.animation === 'slideDown')
|
|
{
|
|
this.$el.height(this.$el.height());
|
|
}
|
|
},
|
|
_buildAnimationOff: function(animation)
|
|
{
|
|
return (this._isHidable(animation)) ? 'hide' : 'show';
|
|
},
|
|
_isAnimate: function()
|
|
{
|
|
return (this.animation !== 'show' && this.animation !== 'hide');
|
|
},
|
|
_isHidable: function(effect)
|
|
{
|
|
return (this.hidableEffects.indexOf(effect) !== -1);
|
|
},
|
|
_clean: function()
|
|
{
|
|
this.$body.removeClass('is-no-scroll-x');
|
|
this.$el.removeClass(this.prefix + this.animation);
|
|
this.$el.removeAttr('kube-animate-effect');
|
|
},
|
|
_toggle: function()
|
|
{
|
|
if (this.animation === 'show') this.$el.show();
|
|
else this.$el.hide();
|
|
|
|
if (typeof this.callback === 'function') this.callback(this);
|
|
},
|
|
_animate: function()
|
|
{
|
|
this.$body.addClass('is-no-scroll-x');
|
|
this.$el.show();
|
|
|
|
this.$el.addClass(this.prefix + this.animation);
|
|
this.$el.attr('kube-animate-effect', this.prefix + this.animation);
|
|
this._complete();
|
|
},
|
|
_complete: function()
|
|
{
|
|
|
|
this.$el.one('animationend webkitAnimationEnd', function(e)
|
|
{
|
|
if (this.$el.hasClass(this.prefix + this.animation)) this._clean();
|
|
if (this._isHidable(this.animation)) this.$el.hide();
|
|
|
|
if (this.animation === 'slideUp' || this.animation === 'slideDown') this.$el.css('height', '');
|
|
if (typeof this.callback === 'function') this.callback(this.$el);
|
|
|
|
}.bind(this));
|
|
}
|
|
};
|
|
$K.add('service', 'transition', {
|
|
init: function(app)
|
|
{
|
|
this.transitionOpt = true;
|
|
},
|
|
run: function(element, params)
|
|
{
|
|
return new $K.TransitionPlay(params, element, this.transitionOpt);
|
|
|
|
},
|
|
remove: function(element)
|
|
{
|
|
this.$el = $K.dom(element);
|
|
|
|
var classname = this.$el.attr('kube-transition-class');
|
|
if (classname)
|
|
{
|
|
this.$el.removeClass(classname);
|
|
this.$el.removeAttr('kube-transition-class');
|
|
}
|
|
|
|
var css = this.$el.attr('kube-transition-css');
|
|
if (css)
|
|
{
|
|
var names = css.split(',');
|
|
for (var i = 0; i < names.length; i++)
|
|
{
|
|
this.$el.css(names[i], '');
|
|
}
|
|
|
|
this.$el.removeAttr('kube-transition-css');
|
|
}
|
|
|
|
this.$el.off('transitionend webkitTransitionEnd');
|
|
}
|
|
});
|
|
|
|
|
|
$K.TransitionPlay = function(params, element, transitionOpt)
|
|
{
|
|
this.$el = $K.dom(element);
|
|
this.params = params;
|
|
|
|
this._transition();
|
|
};
|
|
|
|
$K.TransitionPlay.prototype = {
|
|
_transition: function()
|
|
{
|
|
if (this.params.classname)
|
|
{
|
|
this.$el.addClass(this.params.classname);
|
|
this.$el.attr('kube-transition-class', this.params.classname);
|
|
}
|
|
|
|
if (this.params.css)
|
|
{
|
|
this.$el.css(this.params.css);
|
|
|
|
var names = [];
|
|
for (var key in this.params.css)
|
|
{
|
|
names.push(key);
|
|
}
|
|
|
|
this.$el.attr('kube-transition-css', names.join(','))
|
|
}
|
|
|
|
this._complete();
|
|
},
|
|
_complete: function()
|
|
{
|
|
this.$el.one('transitionend webkitTransitionEnd', function(e)
|
|
{
|
|
if (typeof this.params.callback === 'function') this.params.callback(this.$el);
|
|
|
|
}.bind(this));
|
|
}
|
|
};
|
|
$K.add('service', 'lang', {
|
|
init: function(app)
|
|
{
|
|
this.app = app;
|
|
this.opts = app.opts;
|
|
|
|
var lang = (this.opts.lang) ? this.opts.lang : 'en';
|
|
|
|
// build
|
|
this.vars = this.build(lang);
|
|
},
|
|
build: function(lang)
|
|
{
|
|
lang = ($K.lang[lang] === undefined) ? 'en' : lang;
|
|
|
|
return ($K.lang[lang] !== undefined) ? $K.lang[lang] : [];
|
|
},
|
|
rebuild: function(lang)
|
|
{
|
|
this.opts.lang = lang;
|
|
this.vars = this.build(lang);
|
|
},
|
|
extend: function(obj)
|
|
{
|
|
this.vars = $K.extend(this.vars, obj);
|
|
},
|
|
parse: function(str)
|
|
{
|
|
if (str === undefined)
|
|
{
|
|
return '';
|
|
}
|
|
|
|
var matches = str.match(/## (.*?) ##/g);
|
|
if (matches)
|
|
{
|
|
for (var i = 0; i < matches.length; i++)
|
|
{
|
|
var key = matches[i].replace(/^##\s/g, '').replace(/\s##$/g, '');
|
|
str = str.replace(matches[i], this.get(key));
|
|
}
|
|
}
|
|
|
|
return str;
|
|
},
|
|
get: function(name)
|
|
{
|
|
return (typeof this.vars[name] !== 'undefined') ? this.vars[name] : '';
|
|
}
|
|
});
|
|
$K.add('service', 'options', {
|
|
init: function(app, type, opts)
|
|
{
|
|
this.app = app;
|
|
this.utils = app.utils;
|
|
|
|
return (type === 'global') ? this._build(opts) : this._buildElement(opts);
|
|
},
|
|
_build: function(opts)
|
|
{
|
|
return (opts) ? this._extendFromElements(opts) : {};
|
|
},
|
|
_buildElement: function($el)
|
|
{
|
|
return $K.extend(
|
|
{},
|
|
$el.data()
|
|
);
|
|
},
|
|
_extendFromElements: function(options)
|
|
{
|
|
return (options.hasOwnProperty('append')) ? this.utils.extendData(options, options['append']) : options;
|
|
}
|
|
});
|
|
$K.add('service', 'response', {
|
|
init: function(app)
|
|
{
|
|
this.app = app;
|
|
},
|
|
// public
|
|
parse: function(str)
|
|
{
|
|
if (str === '') return false;
|
|
|
|
var obj = (typeof str === 'object') ? str : JSON.parse(str);
|
|
if (obj[0] !== undefined)
|
|
{
|
|
for (var item in obj)
|
|
{
|
|
this._parseItem(obj[item]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this._parseItem(obj);
|
|
}
|
|
|
|
return obj;
|
|
},
|
|
// private
|
|
_parseItem: function(item)
|
|
{
|
|
if (item.type === 'location')
|
|
{
|
|
top.location.href = item.data;
|
|
}
|
|
else if (item.type === 'message')
|
|
{
|
|
this.message.show(item.data);
|
|
}
|
|
else
|
|
{
|
|
for (var key in item.data)
|
|
{
|
|
var val = item.data[key];
|
|
var $el = $K.dom(key);
|
|
|
|
if (item.type === 'value')
|
|
{
|
|
val = (val === null || val === false) ? 0 : val;
|
|
val = (val === true) ? 1 : val;
|
|
|
|
$el.val(val);
|
|
}
|
|
else if (item.type === 'html')
|
|
{
|
|
val = (val === null || val === false) ? '' : val;
|
|
|
|
$el.html(this._stripslashes(val));
|
|
}
|
|
else if (item.type === 'addClass')
|
|
{
|
|
$el.addClass(val);
|
|
}
|
|
else if (item.type === 'removeClass')
|
|
{
|
|
$el.removeClass(val);
|
|
}
|
|
else if (item.type === 'show')
|
|
{
|
|
$el.removeClass('is-hidden');
|
|
}
|
|
else if (item.type === 'hide')
|
|
{
|
|
$el.addClass('is-hidden');
|
|
}
|
|
else if (item.type === 'animate')
|
|
{
|
|
this.animate.run($el, val);
|
|
}
|
|
}
|
|
}
|
|
|
|
return item;
|
|
},
|
|
_stripslashes: function(str)
|
|
{
|
|
return (str+'').replace(/\0/g, '0').replace(/\\([\\'"])/g, '$1');
|
|
}
|
|
});
|
|
$K.add('service', 'progress', {
|
|
init: function(app)
|
|
{
|
|
this.app = app;
|
|
this.$body = app.$body;
|
|
|
|
// defaults
|
|
this.defaults = {
|
|
selector: 'kube-progress',
|
|
target: false,
|
|
value: 100
|
|
}
|
|
|
|
// local
|
|
this.$progress = false;
|
|
this.$progressBar = false;
|
|
},
|
|
// public
|
|
stop: function()
|
|
{
|
|
this.$progress = false;
|
|
this.$progressBar = false;
|
|
|
|
$K.dom('#' + this.params.selector).remove();
|
|
|
|
if (this.params.target)
|
|
{
|
|
var $target = $K.dom(this.params.target);
|
|
$target.removeClass('is-relative');
|
|
}
|
|
},
|
|
show: function(params)
|
|
{
|
|
this._buildDefaults(params);
|
|
this._build();
|
|
},
|
|
hide: function(params)
|
|
{
|
|
if (this.$progress)
|
|
{
|
|
this._buildDefaults(params);
|
|
this.animate.run(this.$progress, 'fadeOut', this.stop.bind(this));
|
|
}
|
|
},
|
|
update: function(params)
|
|
{
|
|
this._buildDefaults(params);
|
|
|
|
if (!this.$progress) this._build();
|
|
this._setValue();
|
|
},
|
|
|
|
// private
|
|
_buildDefaults: function(data)
|
|
{
|
|
this.params = $K.extend({}, this.defaults, data);
|
|
},
|
|
_build: function()
|
|
{
|
|
this.stop();
|
|
|
|
this.$progress = $K.dom('<div>');
|
|
this.$progress.attr('id', this.params.selector);
|
|
this.$progress.addClass(this.params.selector);
|
|
|
|
this.$progressBar = $K.dom('<span>');
|
|
this.$progress.append(this.$progressBar);
|
|
|
|
if (this.params.target)
|
|
{
|
|
var $target = $K.dom(this.params.target);
|
|
if ($target.css('position') === 'static')
|
|
{
|
|
$target.addClass('is-relative');
|
|
}
|
|
|
|
$target.append(this.$progress);
|
|
}
|
|
else
|
|
{
|
|
this.$progress.addClass('is-fixed');
|
|
this.$body.append(this.$progress);
|
|
}
|
|
},
|
|
_setValue: function()
|
|
{
|
|
this.$progressBar.css('width', this.params.value + '%');
|
|
}
|
|
});
|
|
$K.add('service', 'message', {
|
|
init: function(app)
|
|
{
|
|
this.app = app;
|
|
|
|
// defaults
|
|
this.defaults = {
|
|
name: false,
|
|
delay: 7, // seconds
|
|
message: '',
|
|
position: 'right', // left, centered, line
|
|
positions: ['is-left', 'is-right', 'is-center', 'is-centered', 'is-line'],
|
|
type: false,
|
|
types: ['is-error', 'is-success', 'is-focus', 'is-black'],
|
|
selector: 'kube-message'
|
|
};
|
|
|
|
// animation
|
|
this.currentAnimation = [];
|
|
this.animation = {
|
|
line: ['slideInDown', 'slideOutUp'],
|
|
centered: ['slideInDown', 'slideOutUp'],
|
|
left: ['slideInLeft', 'slideOutLeft'],
|
|
right: ['slideInRight', 'slideOutRight']
|
|
};
|
|
|
|
// local
|
|
this.$message = false;
|
|
this.timeout = false;
|
|
},
|
|
// public
|
|
stop: function()
|
|
{
|
|
clearTimeout(this.timeout);
|
|
|
|
$K.dom('#' + this.params.selector).remove();
|
|
|
|
this.$message = false;
|
|
this.$doc.off('.kube.message');
|
|
},
|
|
show: function(params)
|
|
{
|
|
this._buildDefaults(params);
|
|
|
|
// stop
|
|
this.stop();
|
|
|
|
// build
|
|
this._build();
|
|
this._open();
|
|
},
|
|
hide: function(params)
|
|
{
|
|
this._buildDefaults(params);
|
|
this._close();
|
|
},
|
|
// private
|
|
_broadcast: function(message)
|
|
{
|
|
message = 'message.' + message;
|
|
message = (this.params.name !== false ) ? [this.params.name, message] : message;
|
|
|
|
this.app.broadcast(message, this);
|
|
},
|
|
_buildDefaults: function(data)
|
|
{
|
|
this.params = $K.extend({}, this.defaults, data);
|
|
},
|
|
_buildAnimation: function()
|
|
{
|
|
this.currentAnimation = this.animation[this.params.position];
|
|
},
|
|
_buildClose: function()
|
|
{
|
|
this.$message.on('click.kube.message', this._close.bind(this));
|
|
},
|
|
_buildType: function()
|
|
{
|
|
if (this.params.type)
|
|
{
|
|
this.$message.removeClass(this.params.types.join(' '));
|
|
this.$message.addClass(this.params.type);
|
|
}
|
|
},
|
|
_buildPosition: function()
|
|
{
|
|
this.$message.removeClass(this.params.positions.join(' '));
|
|
this.$message.addClass('is-' + this.params.position);
|
|
},
|
|
_buildMessage: function()
|
|
{
|
|
this.$message.html(this.params.message);
|
|
},
|
|
_build: function()
|
|
{
|
|
this.$message = $K.dom('<div>');
|
|
this.$message.attr('id', this.params.selector);
|
|
this.$message.addClass('message is-hidden');
|
|
|
|
this.$body.append(this.$message);
|
|
},
|
|
_handleKeyboard: function(e)
|
|
{
|
|
if (e.which === 27) this._close();
|
|
},
|
|
_open: function()
|
|
{
|
|
this._broadcast('open');
|
|
|
|
this._buildClose();
|
|
this._buildType();
|
|
this._buildPosition();
|
|
this._buildAnimation();
|
|
this._buildMessage();
|
|
|
|
this.animate.run(this.$message, this.currentAnimation[0], this._opened.bind(this));
|
|
},
|
|
_close: function(e)
|
|
{
|
|
if (this.$message)
|
|
{
|
|
this._broadcast('close');
|
|
this.animate.run(this.$message, this.currentAnimation[1], this._closed.bind(this));
|
|
}
|
|
},
|
|
_opened: function()
|
|
{
|
|
this.$doc.on('keyup.kube.message', this._handleKeyboard.bind(this));
|
|
this.timeout = setTimeout(this._close.bind(this), this.params.delay * 1000);
|
|
|
|
this._broadcast('opened');
|
|
},
|
|
_closed: function()
|
|
{
|
|
this.stop();
|
|
this._broadcast('closed');
|
|
}
|
|
});
|
|
$K.add('service', 'modal', {
|
|
init: function(app)
|
|
{
|
|
this.app = app;
|
|
|
|
// defaults
|
|
this.defaults = {
|
|
target: false,
|
|
name: false,
|
|
url: false,
|
|
title: false,
|
|
width: '600px',
|
|
height: false,
|
|
handle: false,
|
|
commands: false
|
|
};
|
|
|
|
// local
|
|
this.$box = false;
|
|
this.$modal = false;
|
|
|
|
},
|
|
// public
|
|
stop: function()
|
|
{
|
|
if (this.$box)
|
|
{
|
|
this.$box.remove();
|
|
this.$box = false;
|
|
this.$modal = false;
|
|
|
|
this.$doc.off('.kube.modal');
|
|
this.$win.off('.kube.modal');
|
|
}
|
|
|
|
if (this.$overlay)
|
|
{
|
|
this.$overlay.remove();
|
|
}
|
|
},
|
|
open: function(params)
|
|
{
|
|
this._buildDefaults(params);
|
|
|
|
if (this.params.url)
|
|
{
|
|
this._openUrl();
|
|
}
|
|
else if (this.params.target)
|
|
{
|
|
this._openTarget();
|
|
}
|
|
},
|
|
close: function()
|
|
{
|
|
this._close();
|
|
},
|
|
resize: function()
|
|
{
|
|
this.$modal.setWidth(this.params.width);
|
|
this.$modal.updatePosition();
|
|
},
|
|
|
|
// private
|
|
_broadcast: function(message)
|
|
{
|
|
message = 'modal.' + message;
|
|
|
|
this.app.broadcast([this.params.name, message], this, this.$modal, this.$modalForm);
|
|
},
|
|
_isOpened: function()
|
|
{
|
|
return (this.$modal && this.$modal.hasClass('is-open'));
|
|
},
|
|
_openUrl: function()
|
|
{
|
|
$K.ajax.post({
|
|
url: this.params.url,
|
|
success: this._doOpen.bind(this)
|
|
});
|
|
},
|
|
_openTarget: function()
|
|
{
|
|
var template = $K.dom(this.params.target).clone().html();
|
|
this._doOpen(template);
|
|
},
|
|
_doOpen: function(template)
|
|
{
|
|
this.stop();
|
|
|
|
if (!this._isDesktop())
|
|
{
|
|
document.activeElement.blur();
|
|
}
|
|
|
|
|
|
this._createModal(template);
|
|
|
|
this._buildModalBox();
|
|
this._buildOverlay();
|
|
this._buildModal();
|
|
this._buildModalForm();
|
|
this._buildModalCommands();
|
|
|
|
this.$modal.updatePosition();
|
|
this._broadcast('open');
|
|
|
|
this.animate.run(this.$box, 'fadeIn', this._opened.bind(this));
|
|
this.animate.run(this.$overlay, 'fadeIn');
|
|
},
|
|
_opened: function()
|
|
{
|
|
this.$modal.addClass('is-open');
|
|
this.$box.on('mousedown.kube.modal', this._close.bind(this));
|
|
this.$doc.on('keyup.kube.modal', this._handleEscape.bind(this));
|
|
this.$win.on('resize.kube.modal', this.resize.bind(this));
|
|
this.$modal.getBody().find('input[type=text],input[type=url],input[type=email]').on('keydown.kube.modal', this._handleEnter.bind(this));
|
|
|
|
this._broadcast('opened');
|
|
},
|
|
_close: function(e)
|
|
{
|
|
if (!this.$box || !this._isOpened()) return;
|
|
|
|
if (e)
|
|
{
|
|
if (!this._needToClose(e.target))
|
|
{
|
|
return;
|
|
}
|
|
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
}
|
|
|
|
this._broadcast('close');
|
|
|
|
this.animate.run(this.$box, 'fadeOut', this._closed.bind(this));
|
|
this.animate.run(this.$overlay, 'fadeOut');
|
|
},
|
|
_closed: function()
|
|
{
|
|
this.$modal.removeClass('is-open');
|
|
this.$box.off('.kube.modal');
|
|
this.$doc.off('.kube.modal');
|
|
this.$win.off('.kube.modal');
|
|
|
|
this._broadcast('closed');
|
|
},
|
|
_createModal: function(template)
|
|
{
|
|
this.$modal = $K.create('class.modal.element', this.app, template);
|
|
},
|
|
_buildDefaults: function(data)
|
|
{
|
|
this.params = $K.extend({}, this.defaults, data);
|
|
},
|
|
_buildModalBox: function()
|
|
{
|
|
this.$box = $K.dom('<div>');
|
|
this.$box.attr('id', 'kube-modal');
|
|
this.$box.addClass('modal-box is-hidden');
|
|
this.$box.html('');
|
|
this.$body.append(this.$box);
|
|
},
|
|
_buildOverlay: function()
|
|
{
|
|
this.$overlay = $K.dom('#kube-overlay');
|
|
if (this.$overlay.length === 0)
|
|
{
|
|
this.$overlay = $K.dom('<div>');
|
|
this.$overlay.attr('id', 'kube-overlay');
|
|
this.$overlay.addClass('overlay is-hidden');
|
|
this.$body.prepend(this.$overlay);
|
|
}
|
|
},
|
|
_buildModal: function()
|
|
{
|
|
this.$box.append(this.$modal);
|
|
|
|
this.$modal.setTitle(this.params.title);
|
|
this.$modal.setHeight(this.params.height);
|
|
this.$modal.setWidth(this.params.width);
|
|
},
|
|
_buildModalCommands: function()
|
|
{
|
|
if (this.params.commands)
|
|
{
|
|
var commands = this.params.commands;
|
|
var $footer = this.$modal.getFooter();
|
|
for (var key in commands)
|
|
{
|
|
var $btn = $K.dom('<button>');
|
|
|
|
$btn.addClass('button');
|
|
$btn.html(commands[key].title);
|
|
$btn.attr('data-command', key);
|
|
|
|
if (typeof commands[key].classname !== 'undefined')
|
|
{
|
|
$btn.addClass(commands[key].classname);
|
|
}
|
|
|
|
if (typeof commands[key].close !== 'undefined')
|
|
{
|
|
$btn.attr('data-action', 'close');
|
|
$btn.on('click', this._close.bind(this));
|
|
}
|
|
else
|
|
{
|
|
$btn.on('click', this._handleCommand.bind(this));
|
|
}
|
|
|
|
$footer.append($btn);
|
|
}
|
|
}
|
|
},
|
|
_buildModalForm: function()
|
|
{
|
|
this.$modalForm = $K.create('modal.form', this.app, this.$modal.getForm());
|
|
},
|
|
_needToClose: function(el)
|
|
{
|
|
var $target = $K.dom(el);
|
|
if ($target.attr('data-action') === 'close' || this.$modal.isCloseNode(el) || $target.closest('.modal').length === 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
},
|
|
_handleCommand: function(e)
|
|
{
|
|
var $btn = $K.dom(e.target).closest('button');
|
|
var command = $btn.attr('data-command');
|
|
|
|
if (command !== 'cancel') e.preventDefault();
|
|
|
|
this._broadcast(command);
|
|
},
|
|
_handleEnter: function(e)
|
|
{
|
|
if (e.which === 13)
|
|
{
|
|
if (this.params.handle)
|
|
{
|
|
e.preventDefault();
|
|
this._broadcast(this.params.handle);
|
|
}
|
|
}
|
|
},
|
|
_handleEscape: function(e)
|
|
{
|
|
if (e.which === 27) this._close();
|
|
},
|
|
_isDesktop: function()
|
|
{
|
|
return !/(iPhone|iPod|iPad|Android)/.test(navigator.userAgent);
|
|
}
|
|
});
|
|
|
|
$K.add('class', 'modal.form', {
|
|
extends: ['dom'],
|
|
init: function(app, element)
|
|
{
|
|
this.app = app;
|
|
|
|
// build
|
|
this.build(element);
|
|
},
|
|
|
|
// public
|
|
build: function(element)
|
|
{
|
|
this.parse(element);
|
|
},
|
|
getData: function()
|
|
{
|
|
var data = {};
|
|
this.find('[name]').each(function(node)
|
|
{
|
|
var $node = $K.dom(node);
|
|
data[$node.attr('name')] = $node.val();
|
|
});
|
|
|
|
return data;
|
|
},
|
|
setData: function(data)
|
|
{
|
|
this.find('[name]').each(function(node)
|
|
{
|
|
var $node = $K.dom(node);
|
|
var name = $node.attr('name');
|
|
if (data.hasOwnProperty(name))
|
|
{
|
|
if (node.type && node.type === 'checkbox') node.checked = data[name];
|
|
else $node.val(data[name]);
|
|
}
|
|
});
|
|
},
|
|
getItem: function(name)
|
|
{
|
|
return this.find('[name=' + name + ']');
|
|
}
|
|
});
|
|
$K.add('class', 'modal.element', {
|
|
extends: ['dom'],
|
|
init: function(app, template)
|
|
{
|
|
this.app = app;
|
|
this.opts = app.opts;
|
|
this.$win = app.$win;
|
|
|
|
// init
|
|
this._init(template);
|
|
},
|
|
|
|
// get
|
|
getForm: function()
|
|
{
|
|
return this.find('form');
|
|
},
|
|
getHeader: function()
|
|
{
|
|
return this.$modalHeader;
|
|
},
|
|
getBody: function()
|
|
{
|
|
return this.$modalBody;
|
|
},
|
|
getFooter: function()
|
|
{
|
|
return this.$modalFooter;
|
|
},
|
|
|
|
// set
|
|
setTitle: function(title)
|
|
{
|
|
if (title) this.$modalHeader.html(title);
|
|
},
|
|
setWidth: function(width)
|
|
{
|
|
width = (parseInt(width) >= this.$win.width()) ? '96%' : width;
|
|
|
|
this.css('max-width', width);
|
|
},
|
|
setHeight: function(height)
|
|
{
|
|
if (height !== false) this.$modalBody.css('height', height);
|
|
},
|
|
|
|
// update
|
|
updatePosition: function()
|
|
{
|
|
var width = this.width();
|
|
this.css({ 'left': '50%', 'margin-left': '-' + (width/2) + 'px' });
|
|
|
|
var windowHeight = this.$win.height();
|
|
var height = this.height();
|
|
var marginTop = (windowHeight/2 - height/2);
|
|
|
|
if (height < windowHeight && marginTop !== 0)
|
|
{
|
|
this.css('margin-top', marginTop + 'px');
|
|
}
|
|
},
|
|
|
|
// is
|
|
isCloseNode: function(el)
|
|
{
|
|
return (el === this.$modalClose.get());
|
|
},
|
|
|
|
// private
|
|
_init: function(template)
|
|
{
|
|
this._build();
|
|
this._buildClose();
|
|
this._buildHeader();
|
|
this._buildBody();
|
|
this._buildFooter();
|
|
this._buildTemplate(template);
|
|
},
|
|
_build: function()
|
|
{
|
|
this.parse('<div>');
|
|
this.addClass('modal');
|
|
this.attr('dir', this.opts.direction);
|
|
},
|
|
_buildClose: function()
|
|
{
|
|
this.$modalClose = $K.dom('<span>');
|
|
this.$modalClose.addClass('close');
|
|
|
|
this.append(this.$modalClose);
|
|
},
|
|
_buildHeader: function()
|
|
{
|
|
this.$modalHeader = $K.dom('<div>');
|
|
this.$modalHeader.addClass('modal-header');
|
|
|
|
this.append(this.$modalHeader);
|
|
},
|
|
_buildBody: function()
|
|
{
|
|
this.$modalBody = $K.dom('<div>');
|
|
this.$modalBody.addClass('modal-body');
|
|
|
|
this.append(this.$modalBody);
|
|
},
|
|
_buildFooter: function()
|
|
{
|
|
this.$modalFooter = $K.dom('<div>');
|
|
this.$modalFooter.addClass('modal-footer');
|
|
|
|
this.append(this.$modalFooter);
|
|
},
|
|
_buildTemplate: function(template)
|
|
{
|
|
this.$modalBody.html(template);
|
|
}
|
|
});
|
|
$K.add('service', 'observer', {
|
|
init: function(app)
|
|
{
|
|
this.app = app;
|
|
this.opts = app.opts;
|
|
|
|
if (this._isObserve())
|
|
{
|
|
this._build();
|
|
}
|
|
},
|
|
|
|
// private
|
|
_isObserve: function()
|
|
{
|
|
return (typeof this.opts.observer !== 'undefined' && window.MutationObserver);
|
|
},
|
|
_build: function()
|
|
{
|
|
var self = this;
|
|
var observer = new MutationObserver(function(mutations)
|
|
{
|
|
mutations.forEach(function(mutation)
|
|
{
|
|
var newNodes = mutation.addedNodes;
|
|
if (newNodes.length === 0 || (newNodes.length === 1 && newNodes.nodeType === 3))
|
|
{
|
|
return;
|
|
}
|
|
|
|
self._iterate();
|
|
});
|
|
});
|
|
|
|
// pass in the target node, as well as the observer options
|
|
observer.observe(document, {
|
|
subtree: true,
|
|
childList: true
|
|
});
|
|
},
|
|
_iterate: function()
|
|
{
|
|
var self = this;
|
|
var $nodes = $K.dom('[data-kube]').not('[data-loaded]');
|
|
$nodes.each(function(node, i)
|
|
{
|
|
var $el = $K.dom(node);
|
|
var name = $el.attr('data-kube');
|
|
var id = ($el.attr('id')) ? $el.attr('id') : name + '-' + (self.app.servicesIndex + i);
|
|
var instance = new App.Module(self.app, $el, name, id);
|
|
|
|
self._storeElementModule(instance, name, id)
|
|
self._call(instance, 'start');
|
|
});
|
|
|
|
// $R
|
|
if (typeof $R !== 'undefined')
|
|
{
|
|
$R('[data-redactor]');
|
|
}
|
|
},
|
|
_call: function(instance, method, args)
|
|
{
|
|
if (typeof instance[method] === 'function')
|
|
{
|
|
return instance[method].apply(instance, args);
|
|
}
|
|
},
|
|
_storeElementModule: function(instance, name, id)
|
|
{
|
|
if (instance)
|
|
{
|
|
if (typeof this.app.modules[name] === 'undefined')
|
|
{
|
|
this.app.modules[name] = {};
|
|
}
|
|
|
|
this.app.modules[name][id] = instance;
|
|
}
|
|
}
|
|
});
|
|
$K.add('service', 'utils', {
|
|
init: function(app)
|
|
{
|
|
this.app = app;
|
|
},
|
|
|
|
// string
|
|
parseOptsString: function(str)
|
|
{
|
|
var properties = str.replace('{', '').replace('}', '').trim().replace(/;$/, '').split(';');
|
|
var obj = {};
|
|
properties.forEach(function(property) {
|
|
var tup = property.split(':');
|
|
obj[tup[0].trim()] = tup[1].trim().replace(/'/g, '');
|
|
});
|
|
|
|
return obj;
|
|
},
|
|
ucfirst: function(str)
|
|
{
|
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
},
|
|
|
|
// object
|
|
checkProperty: function(obj)
|
|
{
|
|
var args = (arguments[1] && Array.isArray(arguments[1])) ? arguments[1] : [].slice.call(arguments, 1);
|
|
|
|
for (var i = 0; i < args.length; i++)
|
|
{
|
|
if (!obj || (typeof obj[args[i]] === 'undefined'))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
obj = obj[args[i]];
|
|
}
|
|
|
|
return obj;
|
|
},
|
|
|
|
// data
|
|
extendData: function(data, elements)
|
|
{
|
|
if (typeof elements === 'object')
|
|
{
|
|
data = $K.extend({}, data, elements);
|
|
}
|
|
else if (typeof elements === 'string')
|
|
{
|
|
var $elms = $K.dom(elements);
|
|
$elms.each(function(node)
|
|
{
|
|
var $node = $K.dom(node);
|
|
if (node.tagName === 'FORM')
|
|
{
|
|
data = $K.extend({}, data, $node.serialize(true));
|
|
}
|
|
else
|
|
{
|
|
var name = ($node.attr('name')) ? $node.attr('name') : $node.attr('id');
|
|
var val = $node.val();
|
|
data[name] = (this._isNumber(val)) ? parseFloat(val) : this._getBooleanFromStr(val);
|
|
}
|
|
});
|
|
}
|
|
|
|
return data;
|
|
},
|
|
_isNumber: function(str)
|
|
{
|
|
return !isNaN(str) && !isNaN(parseFloat(str));
|
|
},
|
|
_getBooleanFromStr: function(str)
|
|
{
|
|
if (str === 'true') return true;
|
|
else if (str === 'false') return false;
|
|
|
|
return str;
|
|
}
|
|
});
|
|
|
|
window.Kube = window.$K = $K;
|
|
}());
|
|
(function($K)
|
|
{
|
|
$K.add('module', 'alert', {
|
|
init: function(app, context)
|
|
{
|
|
this.app = app;
|
|
this.animate = app.animate;
|
|
|
|
// context
|
|
this.context = context;
|
|
this.$element = context.getElement();
|
|
},
|
|
// events
|
|
onclick: function(e, element, type)
|
|
{
|
|
if (type === 'close')
|
|
{
|
|
this.close(e);
|
|
}
|
|
},
|
|
// public
|
|
open: function(e)
|
|
{
|
|
if (this.$element.isOpened()) return;
|
|
if (e) e.preventDefault();
|
|
|
|
this.app.broadcast('alert.open', this);
|
|
this.animate.run(this.$element, 'fadeIn', this._opened.bind(this));
|
|
},
|
|
close: function(e)
|
|
{
|
|
if (this.$element.isClosed()) return;
|
|
if (e) e.preventDefault();
|
|
|
|
this.app.broadcast('alert.close', this);
|
|
this.animate.run(this.$element, 'fadeOut', this._closed.bind(this));
|
|
},
|
|
// private
|
|
_opened: function()
|
|
{
|
|
this.app.broadcast('alert.opened', this);
|
|
},
|
|
_closed: function()
|
|
{
|
|
this.app.broadcast('alert.closed', this);
|
|
}
|
|
});
|
|
})(Kube);
|
|
(function($K)
|
|
{
|
|
$K.add('module', 'toggle', {
|
|
init: function(app, context)
|
|
{
|
|
this.app = app;
|
|
this.animate = app.animate;
|
|
|
|
// defaults
|
|
var defaults = {
|
|
target: false
|
|
};
|
|
|
|
// context
|
|
this.context = context;
|
|
this.params = context.getParams(defaults);
|
|
this.$element = context.getElement();
|
|
this.$target = context.getTarget();
|
|
},
|
|
// public
|
|
start: function()
|
|
{
|
|
this.$element.on('click.kube.toggle', this.toggle.bind(this));
|
|
},
|
|
stop: function()
|
|
{
|
|
this.$element.off('.kube.toggle');
|
|
},
|
|
toggle: function(e)
|
|
{
|
|
return (this.$target.isOpened()) ? this.close(e) : this.open(e);
|
|
},
|
|
open: function(e)
|
|
{
|
|
if (this.$target.isOpened()) return;
|
|
if (e) e.preventDefault();
|
|
|
|
this.app.broadcast('toggle.open', this);
|
|
this.animate.run(this.$target, 'slideDown', this._opened.bind(this));
|
|
},
|
|
close: function(e)
|
|
{
|
|
if (this.$target.isClosed()) return;
|
|
if (e) e.preventDefault();
|
|
|
|
this.app.broadcast('toggle.close', this);
|
|
this.animate.run(this.$target, 'slideUp', this._closed.bind(this));
|
|
},
|
|
|
|
// private
|
|
_opened: function()
|
|
{
|
|
this.app.broadcast('toggle.opened', this);
|
|
},
|
|
_closed: function()
|
|
{
|
|
this.app.broadcast('toggle.closed', this);
|
|
}
|
|
});
|
|
})(Kube);
|
|
(function($K)
|
|
{
|
|
$K.add('module', 'sticky', {
|
|
init: function(app, context)
|
|
{
|
|
this.app = app;
|
|
this.$win = app.$win;
|
|
|
|
// defaults
|
|
var defaults = {
|
|
offset: 0 // string in pixels
|
|
};
|
|
|
|
// context
|
|
this.context = context;
|
|
this.params = context.getParams(defaults);
|
|
this.$element = context.getElement();
|
|
},
|
|
start: function()
|
|
{
|
|
this.offsetTop = this._getOffsetTop();
|
|
|
|
this._load();
|
|
this.$win.on('scroll.kube.sticky', this._load.bind(this));
|
|
},
|
|
stop: function()
|
|
{
|
|
this.$win.off('scroll.kube.sticky');
|
|
this.$element.removeClass('fixed').css('top', '');
|
|
},
|
|
// private
|
|
_load: function()
|
|
{
|
|
return (this._isFix()) ? this._setFixed() : this._setUnfixed();
|
|
},
|
|
_isFix: function()
|
|
{
|
|
return (this.$win.scrollTop() > (this.offsetTop + parseInt(this.params.offset, 10)));
|
|
},
|
|
_setFixed: function()
|
|
{
|
|
this.$element.addClass('is-fixed').css('top', this.params.offset);
|
|
this.app.broadcast('sticky.fixed', this);
|
|
},
|
|
_setUnfixed: function()
|
|
{
|
|
this.$element.removeClass('is-fixed').css('top', '');
|
|
this.app.broadcast('sticky.unfixed', this);
|
|
},
|
|
_getOffsetTop: function()
|
|
{
|
|
return this.$element.offset().top;
|
|
}
|
|
});
|
|
})(Kube);
|
|
(function($K)
|
|
{
|
|
$K.add('module', 'offcanvas', {
|
|
init: function(app, context)
|
|
{
|
|
this.app = app;
|
|
this.$doc = app.$doc;
|
|
this.$body = app.$body;
|
|
this.utils = app.utils;
|
|
this.animate = app.animate;
|
|
this.transition = app.transition;
|
|
|
|
// defaults
|
|
var defaults = {
|
|
clickOutside: true,
|
|
target: false
|
|
};
|
|
|
|
// context
|
|
this.context = context;
|
|
this.params = context.getParams(defaults);
|
|
this.$element = context.getElement();
|
|
this.$target = context.getTarget();
|
|
|
|
// build
|
|
this._build();
|
|
},
|
|
start: function()
|
|
{
|
|
this.$element.on('click.kube.offcanvas', this.toggle.bind(this));
|
|
},
|
|
stop: function()
|
|
{
|
|
this._clear();
|
|
},
|
|
toggle: function(e)
|
|
{
|
|
return (this.$target.isOpened()) ? this.close(e) : this.open(e);
|
|
},
|
|
open: function(e)
|
|
{
|
|
if (e)
|
|
{
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
}
|
|
|
|
this._clear();
|
|
|
|
this.$body.addClass('is-no-scroll-x');
|
|
this.$target.addClass('is-offcanvas');
|
|
|
|
this.targetWidth = this.$target.width();
|
|
|
|
this._resize();
|
|
this.app.broadcast('offcanvas.open', this);
|
|
|
|
return (this.isSlide) ? this._openSlide() : this._openPush();
|
|
},
|
|
close: function(e)
|
|
{
|
|
if (this.eventScroll) return;
|
|
if (e)
|
|
{
|
|
var $el = $K.dom(e.target);
|
|
var el = $el.get();
|
|
var isClickable = (el.tagName === 'A' ||el.tagName === 'BUTTON');
|
|
if (!isClickable || el === this.$element.get())
|
|
{
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
|
|
this.app.broadcast('offcanvas.close', this);
|
|
|
|
return (this.isSlide) ? this._closeSlide() : this._closePush();
|
|
},
|
|
// private
|
|
_build: function()
|
|
{
|
|
this.isSlide = !(this.$target.hasClass('is-offcanvas-push'));
|
|
this.slideDirection = (this.$target.hasClass('is-offcanvas-right')) ? 'Right' : 'Left';
|
|
this.pushSign = (this.slideDirection === 'Left') ? '' : '-';
|
|
this.eventScroll = false;
|
|
},
|
|
_handleKeyboard: function(e)
|
|
{
|
|
if (e.which === 27) this.close();
|
|
},
|
|
|
|
_openSlide: function()
|
|
{
|
|
this.animate.run(this.$target, 'slideIn' + this.slideDirection, this._opened.bind(this));
|
|
},
|
|
_openPush: function()
|
|
{
|
|
this.$target.show();
|
|
this._pushBody(this.pushSign + this.targetWidth + 'px', this._opened.bind(this));
|
|
},
|
|
_opened: function()
|
|
{
|
|
this.$doc.on('touchmove.kube.offcanvas', function() { this.eventScroll = true; }.bind(this));
|
|
this.$doc.on('touchstart.kube.offcanvas', function() { this.eventScroll = false; }.bind(this));
|
|
this.$doc.on('keyup.kube.offcanvas', this._handleKeyboard.bind(this));
|
|
|
|
if (this.params.clickOutside)
|
|
{
|
|
this.$doc.on('click.kube.offcanvas touchend.kube.offcanvas', this.close.bind(this));
|
|
}
|
|
|
|
this.app.broadcast('offcanvas.opened', this);
|
|
},
|
|
_closeSlide: function()
|
|
{
|
|
this.animate.run(this.$target, 'slideOut' + this.slideDirection, this._closed.bind(this));
|
|
},
|
|
_closePush: function()
|
|
{
|
|
this._pushBody('0', this._closed.bind(this));
|
|
},
|
|
_closed: function()
|
|
{
|
|
this.$doc.off('.kube.offcanvas');
|
|
this.$body.removeClass('is-no-scroll-x');
|
|
this.transition.remove(this.$body);
|
|
this.$target.removeClass('is-offcanvas');
|
|
this.$target.hide();
|
|
|
|
this.app.broadcast('offcanvas.closed', this);
|
|
},
|
|
_pushBody: function(transform, callback)
|
|
{
|
|
var params = {
|
|
classname: 'is-offcanvasTransition',
|
|
css: { transform: 'translateX(' + transform + ')' },
|
|
callback: callback
|
|
};
|
|
|
|
this.transition.run(this.$body, params, callback);
|
|
},
|
|
_resize: function()
|
|
{
|
|
var resize = function()
|
|
{
|
|
this.$target.height(this.$doc.height());
|
|
}.bind(this);
|
|
|
|
resize();
|
|
this.$doc.on('resize.kube.offcanvas', resize);
|
|
},
|
|
_clear: function()
|
|
{
|
|
this.$doc.off('.kube.offcanvas');
|
|
this.transition.remove(this.$body);
|
|
|
|
$K.dom('.is-offcanvas').each(function(node)
|
|
{
|
|
var $el = $K.dom(node);
|
|
|
|
this.animate.remove($el);
|
|
|
|
$el.hide();
|
|
$el.removeClass('is-offcanvas');
|
|
|
|
}.bind(this));
|
|
}
|
|
});
|
|
})(Kube);
|
|
(function($K)
|
|
{
|
|
$K.add('module', 'tabs', {
|
|
init: function(app, context)
|
|
{
|
|
this.app = app;
|
|
this.$body = app.$body;
|
|
|
|
// defaults
|
|
var defaults = {
|
|
equal: false
|
|
};
|
|
|
|
// context
|
|
this.context = context;
|
|
this.params = context.getParams(defaults);
|
|
this.$element = context.getElement();
|
|
|
|
// local
|
|
this.$boxes = $K.dom([]);
|
|
this.$tabActive = false;
|
|
this.$boxActive = false;
|
|
},
|
|
start: function()
|
|
{
|
|
this._buildControls();
|
|
this._buildBoxes();
|
|
this._setEqualBoxes();
|
|
|
|
this._open();
|
|
},
|
|
stop: function()
|
|
{
|
|
this.$tabsControls.off('.kube.tabs');
|
|
},
|
|
// api
|
|
getActiveTab: function()
|
|
{
|
|
return this.$tabActive;
|
|
},
|
|
getActiveBox: function()
|
|
{
|
|
return this.$boxActive;
|
|
},
|
|
// private
|
|
_toggle: function(e)
|
|
{
|
|
if (e)
|
|
{
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
}
|
|
|
|
var $tab = $K.dom(e.target);
|
|
var $box = this._getBox($tab);
|
|
|
|
if ($tab.hasClass('is-active')) return;
|
|
|
|
this._open($tab);
|
|
this.app.broadcast('tabs.opened', this);
|
|
},
|
|
_buildControls: function()
|
|
{
|
|
this.$tabsControls = this.$element.find('a');
|
|
this.$tabsControls.on('click.kube.tabs', this._toggle.bind(this));
|
|
},
|
|
_buildBoxes: function()
|
|
{
|
|
this.$tabsControls.each(function(node, i)
|
|
{
|
|
var $tab = $K.dom(node);
|
|
var $box = this._getBox($tab);
|
|
|
|
this.$boxes.add($box);
|
|
|
|
if (i === 0) this.$tabActive = $tab;
|
|
if ($tab.hasClass('is-active')) this.$tabActive = $tab;
|
|
|
|
}.bind(this));
|
|
},
|
|
_open: function($tab)
|
|
{
|
|
this.$tabActive = ($tab) ? $tab : this.$tabActive;
|
|
|
|
this.$tabsControls.removeClass('is-active');
|
|
this.$tabActive.addClass('is-active');
|
|
this.$boxActive = this._getBox(this.$tabActive);
|
|
|
|
this.$boxes.addClass('is-hidden').removeClass('is-open');
|
|
this.$boxActive.removeClass('is-hidden').addClass('is-open');
|
|
},
|
|
_getBox: function($tab)
|
|
{
|
|
return $K.dom($tab.attr('href'));
|
|
},
|
|
_setEqualBoxes: function()
|
|
{
|
|
if (!this.params.equal) return;
|
|
|
|
var minHeight = this._getItemMaxHeight() + 'px';
|
|
|
|
this.$boxes.css('min-height', minHeight);
|
|
},
|
|
_getItemMaxHeight: function()
|
|
{
|
|
var max = 0;
|
|
this.$boxes.each(function(node)
|
|
{
|
|
var $node = $K.dom(node);
|
|
var h = $node.height();
|
|
max = (h > max) ? h : max;
|
|
});
|
|
|
|
return max;
|
|
}
|
|
});
|
|
})(Kube);
|
|
(function($K)
|
|
{
|
|
$K.add('module', 'dropdown', {
|
|
init: function(app, context)
|
|
{
|
|
this.app = app;
|
|
this.$doc = app.$doc;
|
|
this.$win = app.$win;
|
|
this.$body = app.$body;
|
|
this.utils = app.utils;
|
|
this.animate = app.animate;
|
|
|
|
// defaults
|
|
var defaults = {
|
|
target: false
|
|
};
|
|
|
|
// context
|
|
this.context = context;
|
|
this.params = context.getParams(defaults);
|
|
this.$element = context.getElement();
|
|
this.$target = context.getTarget();
|
|
|
|
// local
|
|
this.animationOpen = 'slideDown';
|
|
this.animationClose = 'slideUp';
|
|
},
|
|
// public
|
|
start: function()
|
|
{
|
|
this.$element.on('click.kube.dropdown', this.toggle.bind(this));
|
|
},
|
|
stop: function()
|
|
{
|
|
this.animate.clear(this.$target);
|
|
this.$target.hide();
|
|
|
|
this.$element.off('.kube.dropdown');
|
|
this.$doc.off('.kube.dropdown');
|
|
this.$win.off('.kube.dropdown');
|
|
},
|
|
toggle: function(e)
|
|
{
|
|
return (this.$target.isOpened()) ? this.close(e) : this.open(e);
|
|
},
|
|
open: function(e)
|
|
{
|
|
if (this.$target.isOpened()) return;
|
|
if (e)
|
|
{
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
}
|
|
|
|
this.$doc.off('.kube.dropdown');
|
|
this.$win.off('.kube.dropdown');
|
|
|
|
// hide all
|
|
this.$body.find('.dropdown').each(function(node)
|
|
{
|
|
var $el = $K.dom(node);
|
|
|
|
this.animate.remove($el);
|
|
$el.hide();
|
|
|
|
}.bind(this));
|
|
|
|
this._openCaret();
|
|
this._setPosition();
|
|
|
|
this.$element.addClass('dropdown-in');
|
|
this.app.broadcast('dropdown.open', this);
|
|
this.animate.run(this.$target, this.animationOpen, this._opened.bind(this));
|
|
},
|
|
close: function(e)
|
|
{
|
|
if (this.$target.isClosed()) return;
|
|
if (e)
|
|
{
|
|
var el = e.target;
|
|
var $el = $K.dom(el);
|
|
var isClickable = (el.tagName === 'A' || el.tagName === 'BUTTON');
|
|
if (!isClickable || el === this.$element.get() || (el.tagName === 'A' && $el.hasClass('is-active')))
|
|
{
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
|
|
this.app.broadcast('dropdown.close', this);
|
|
this.animate.run(this.$target, this.animationClose, this._closed.bind(this));
|
|
},
|
|
|
|
// private
|
|
_getPlacement: function()
|
|
{
|
|
var pos = this.$element.position();
|
|
var height = parseFloat(this.$element.css('height')) + pos.top + parseFloat(this.$target.css('height'));
|
|
return (this.$doc.height() < height) ? 'top' : 'bottom';
|
|
},
|
|
_setPosition: function()
|
|
{
|
|
var elHeight = parseFloat(this.$element.css('height'));
|
|
var pos = this.$element.offset();
|
|
var top = pos.top + elHeight;
|
|
var left = pos.left;
|
|
var height = parseFloat(this.$target.css('height'));
|
|
var placement = this._getPlacement();
|
|
var width = parseFloat(this.$target.css('width'));
|
|
var borderWidth = parseFloat(this.$element.css('border-left-width')) + parseFloat(this.$element.css('border-right-width'));
|
|
var leftFix = (this.$win.width() < (left + width)) ? (width - this.$element.width() - borderWidth) : 0;
|
|
|
|
if (placement === 'top')
|
|
{
|
|
top = top - height - elHeight;
|
|
this.animationOpen = 'show';
|
|
this.animationClose = 'hide';
|
|
}
|
|
else
|
|
{
|
|
this.animationOpen = 'slideDown';
|
|
this.animationClose = 'slideUp';
|
|
}
|
|
|
|
this.$target.css({ 'top': top + 'px', 'left': (left - leftFix) + 'px' });
|
|
},
|
|
_handleKeyboard: function(e)
|
|
{
|
|
if (e.which === 27) this.close();
|
|
},
|
|
_opened: function()
|
|
{
|
|
this.$doc.on('keyup.kube.dropdown', this._handleKeyboard.bind(this));
|
|
this.$doc.on('click.kube.dropdown touchstart.kube.dropdown', this.close.bind(this));
|
|
this.$doc.on('scroll.kube.dropdown', this._setPosition.bind(this));
|
|
this.$win.on('resize.kube.dropdown', this._setPosition.bind(this));
|
|
|
|
this.app.broadcast('dropdown.opened', this);
|
|
},
|
|
_closed: function()
|
|
{
|
|
this.$doc.off('.kube.dropdown');
|
|
this.$win.off('.kube.dropdown');
|
|
|
|
this._closeCaret();
|
|
this.$element.removeClass('dropdown-in');
|
|
this.app.broadcast('dropdown.closed', this);
|
|
},
|
|
_openCaret: function()
|
|
{
|
|
var $caret = this.$element.find('.caret');
|
|
$caret.removeClass('is-down').addClass('is-left');
|
|
},
|
|
_closeCaret: function()
|
|
{
|
|
var $caret = this.$element.find('.caret');
|
|
$caret.removeClass('is-left').addClass('is-down');
|
|
}
|
|
});
|
|
})(Kube); |