You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4025 lines
104 KiB

  1. /*
  2. Kube UI Framework
  3. Version 7.2.1
  4. Updated: November 10, 2018
  5. http://imperavi.com/kube/
  6. Copyright (c) 2009-2018, Imperavi LLC.
  7. License: MIT
  8. */
  9. (function() {
  10. var Ajax = {};
  11. Ajax.settings = {};
  12. Ajax.post = function(options) { return new AjaxRequest('post', options); };
  13. Ajax.get = function(options) { return new AjaxRequest('get', options); };
  14. var AjaxRequest = function(method, options)
  15. {
  16. var defaults = {
  17. method: method,
  18. url: '',
  19. before: function() {},
  20. success: function() {},
  21. error: function() {},
  22. data: false,
  23. async: true,
  24. headers: {}
  25. };
  26. this.p = this.extend(defaults, options);
  27. this.p = this.extend(this.p, Ajax.settings);
  28. this.p.method = this.p.method.toUpperCase();
  29. this.prepareData();
  30. this.xhr = new XMLHttpRequest();
  31. this.xhr.open(this.p.method, this.p.url, this.p.async);
  32. this.setHeaders();
  33. var before = (typeof this.p.before === 'function') ? this.p.before(this.xhr) : true;
  34. if (before !== false)
  35. {
  36. this.send();
  37. }
  38. };
  39. AjaxRequest.prototype = {
  40. extend: function(obj1, obj2)
  41. {
  42. if (obj2) for (var name in obj2) { obj1[name] = obj2[name]; }
  43. return obj1;
  44. },
  45. prepareData: function()
  46. {
  47. if (this.p.method === 'POST' && !this.isFormData()) this.p.headers['Content-Type'] = 'application/x-www-form-urlencoded';
  48. if (typeof this.p.data === 'object' && !this.isFormData()) this.p.data = this.toParams(this.p.data);
  49. if (this.p.method === 'GET') this.p.url = (this.p.data) ? this.p.url + '?' + this.p.data : this.p.url;
  50. },
  51. setHeaders: function()
  52. {
  53. this.xhr.setRequestHeader('X-Requested-With', this.p.headers['X-Requested-With'] || 'XMLHttpRequest');
  54. for (var name in this.p.headers)
  55. {
  56. this.xhr.setRequestHeader(name, this.p.headers[name]);
  57. }
  58. },
  59. isFormData: function()
  60. {
  61. return (typeof window.FormData !== 'undefined' && this.p.data instanceof window.FormData);
  62. },
  63. isComplete: function()
  64. {
  65. return !(this.xhr.status < 200 || this.xhr.status >= 300 && this.xhr.status !== 304);
  66. },
  67. send: function()
  68. {
  69. if (this.p.async)
  70. {
  71. this.xhr.onload = this.loaded.bind(this);
  72. this.xhr.send(this.p.data);
  73. }
  74. else
  75. {
  76. this.xhr.send(this.p.data);
  77. this.loaded.call(this);
  78. }
  79. },
  80. loaded: function()
  81. {
  82. if (this.isComplete())
  83. {
  84. var response = this.xhr.response;
  85. var json = this.parseJson(response);
  86. response = (json) ? json : response;
  87. if (typeof this.p.success === 'function') this.p.success(response, this.xhr);
  88. }
  89. else
  90. {
  91. if (typeof this.p.error === 'function') this.p.error(this.xhr.statusText);
  92. }
  93. },
  94. parseJson: function(str)
  95. {
  96. try {
  97. var o = JSON.parse(str);
  98. if (o && typeof o === 'object')
  99. {
  100. return o;
  101. }
  102. } catch (e) {}
  103. return false;
  104. },
  105. toParams: function (obj)
  106. {
  107. return Object.keys(obj).map(
  108. function(k){ return encodeURIComponent(k) + '=' + encodeURIComponent(obj[k]); }
  109. ).join('&');
  110. }
  111. };
  112. var DomCache = [0];
  113. var DomExpando = 'data' + +new Date();
  114. var DomHClass = 'is-hidden';
  115. var DomHMClass = 'is-hidden-mobile';
  116. var Dom = function(selector, context)
  117. {
  118. return this.parse(selector, context);
  119. };
  120. Dom.ready = function(fn)
  121. {
  122. if (document.readyState != 'loading') fn();
  123. else document.addEventListener('DOMContentLoaded', fn);
  124. };
  125. Dom.prototype = {
  126. get dom()
  127. {
  128. return true;
  129. },
  130. get length()
  131. {
  132. return this.nodes.length;
  133. },
  134. parse: function(selector, context)
  135. {
  136. var nodes;
  137. var reHtmlTest = /^\s*<(\w+|!)[^>]*>/;
  138. if (!selector)
  139. {
  140. nodes = [];
  141. }
  142. else if (selector.dom)
  143. {
  144. this.nodes = selector.nodes;
  145. return selector;
  146. }
  147. else if (typeof selector !== 'string')
  148. {
  149. if (selector.nodeType && selector.nodeType === 11)
  150. {
  151. nodes = selector.childNodes;
  152. }
  153. else
  154. {
  155. nodes = (selector.nodeType || selector === window) ? [selector] : selector;
  156. }
  157. }
  158. else if (reHtmlTest.test(selector))
  159. {
  160. nodes = this.create(selector);
  161. }
  162. else
  163. {
  164. nodes = this._query(selector, context);
  165. }
  166. this.nodes = this._slice(nodes);
  167. },
  168. create: function(html)
  169. {
  170. if (/^<(\w+)\s*\/?>(?:<\/\1>|)$/.test(html))
  171. {
  172. return [document.createElement(RegExp.$1)];
  173. }
  174. var elements = [];
  175. var container = document.createElement('div');
  176. var children = container.childNodes;
  177. container.innerHTML = html;
  178. for (var i = 0, l = children.length; i < l; i++)
  179. {
  180. elements.push(children[i]);
  181. }
  182. return elements;
  183. },
  184. // add
  185. add: function(nodes)
  186. {
  187. this.nodes = this.nodes.concat(this._toArray(nodes));
  188. },
  189. // get
  190. get: function(index)
  191. {
  192. return this.nodes[(index || 0)] || false;
  193. },
  194. getAll: function()
  195. {
  196. return this.nodes;
  197. },
  198. eq: function(index)
  199. {
  200. return new Dom(this.nodes[index]);
  201. },
  202. first: function()
  203. {
  204. return new Dom(this.nodes[0]);
  205. },
  206. last: function()
  207. {
  208. return new Dom(this.nodes[this.nodes.length - 1]);
  209. },
  210. contents: function()
  211. {
  212. return this.get().childNodes;
  213. },
  214. // loop
  215. each: function(callback)
  216. {
  217. var len = this.nodes.length;
  218. for (var i = 0; i < len; i++)
  219. {
  220. callback.call(this, (this.nodes[i].dom) ? this.nodes[i].get() : this.nodes[i], i);
  221. }
  222. return this;
  223. },
  224. // traversing
  225. is: function(selector)
  226. {
  227. return (this.filter(selector).length > 0);
  228. },
  229. filter: function (selector)
  230. {
  231. var callback;
  232. if (selector === undefined)
  233. {
  234. return this;
  235. }
  236. else if (typeof selector === 'function')
  237. {
  238. callback = selector;
  239. }
  240. else
  241. {
  242. callback = function(node)
  243. {
  244. if (selector instanceof Node)
  245. {
  246. return (selector === node);
  247. }
  248. else if (selector && selector.dom)
  249. {
  250. return ((selector.nodes).indexOf(node) !== -1);
  251. }
  252. else
  253. {
  254. node.matches = node.matches || node.msMatchesSelector || node.webkitMatchesSelector;
  255. return (node.nodeType === 1) ? node.matches(selector || '*') : false;
  256. }
  257. };
  258. }
  259. return new Dom(this.nodes.filter(callback));
  260. },
  261. not: function(filter)
  262. {
  263. return this.filter(function(node)
  264. {
  265. return !new Dom(node).is(filter || true);
  266. });
  267. },
  268. find: function(selector)
  269. {
  270. var nodes = [];
  271. this.each(function(node)
  272. {
  273. var ns = this._query(selector || '*', node);
  274. for (var i = 0; i < ns.length; i++)
  275. {
  276. nodes.push(ns[i]);
  277. }
  278. });
  279. return new Dom(nodes);
  280. },
  281. children: function(selector)
  282. {
  283. var nodes = [];
  284. this.each(function(node)
  285. {
  286. if (node.children)
  287. {
  288. var ns = node.children;
  289. for (var i = 0; i < ns.length; i++)
  290. {
  291. nodes.push(ns[i]);
  292. }
  293. }
  294. });
  295. return new Dom(nodes).filter(selector);
  296. },
  297. parent: function(selector)
  298. {
  299. var nodes = [];
  300. this.each(function(node)
  301. {
  302. if (node.parentNode) nodes.push(node.parentNode);
  303. });
  304. return new Dom(nodes).filter(selector);
  305. },
  306. parents: function(selector, context)
  307. {
  308. context = this._getContext(context);
  309. var nodes = [];
  310. this.each(function(node)
  311. {
  312. var parent = node.parentNode;
  313. while (parent && parent !== context)
  314. {
  315. if (selector)
  316. {
  317. if (new Dom(parent).is(selector)) { nodes.push(parent); }
  318. }
  319. else
  320. {
  321. nodes.push(parent);
  322. }
  323. parent = parent.parentNode;
  324. }
  325. });
  326. return new Dom(nodes);
  327. },
  328. closest: function(selector, context)
  329. {
  330. context = this._getContext(context);
  331. selector = (selector.dom) ? selector.get() : selector;
  332. var nodes = [];
  333. var isNode = (selector && selector.nodeType);
  334. this.each(function(node)
  335. {
  336. do {
  337. if ((isNode && node === selector) || new Dom(node).is(selector)) return nodes.push(node);
  338. } while ((node = node.parentNode) && node !== context);
  339. });
  340. return new Dom(nodes);
  341. },
  342. next: function(selector)
  343. {
  344. return this._getSibling(selector, 'nextSibling');
  345. },
  346. nextElement: function(selector)
  347. {
  348. return this._getSibling(selector, 'nextElementSibling');
  349. },
  350. prev: function(selector)
  351. {
  352. return this._getSibling(selector, 'previousSibling');
  353. },
  354. prevElement: function(selector)
  355. {
  356. return this._getSibling(selector, 'previousElementSibling');
  357. },
  358. // css
  359. css: function(name, value)
  360. {
  361. if (value === undefined && (typeof name !== 'object'))
  362. {
  363. var node = this.get();
  364. if (name === 'width' || name === 'height')
  365. {
  366. return (node.style) ? this._getHeightOrWidth(name, node, false) + 'px' : undefined;
  367. }
  368. else
  369. {
  370. return (node.style) ? getComputedStyle(node, null)[name] : undefined;
  371. }
  372. }
  373. // set
  374. return this.each(function(node)
  375. {
  376. var obj = {};
  377. if (typeof name === 'object') obj = name;
  378. else obj[name] = value;
  379. for (var key in obj)
  380. {
  381. if (node.style) node.style[key] = obj[key];
  382. }
  383. });
  384. },
  385. // attr
  386. attr: function(name, value, data)
  387. {
  388. data = (data) ? 'data-' : '';
  389. if (value === undefined && (typeof name !== 'object'))
  390. {
  391. var node = this.get();
  392. if (node && node.nodeType !== 3)
  393. {
  394. return (name === 'checked') ? node.checked : this._getBooleanFromStr(node.getAttribute(data + name));
  395. }
  396. else return;
  397. }
  398. // set
  399. return this.each(function(node)
  400. {
  401. var obj = {};
  402. if (typeof name === 'object') obj = name;
  403. else obj[name] = value;
  404. for (var key in obj)
  405. {
  406. if (node.nodeType !== 3)
  407. {
  408. if (key === 'checked') node.checked = obj[key];
  409. else node.setAttribute(data + key, obj[key]);
  410. }
  411. }
  412. });
  413. },
  414. data: function(name, value)
  415. {
  416. if (name === undefined)
  417. {
  418. var reDataAttr = /^data\-(.+)$/;
  419. var attrs = this.get().attributes;
  420. var data = {};
  421. var replacer = function (g) { return g[1].toUpperCase(); };
  422. for (var key in attrs)
  423. {
  424. if (attrs[key] && reDataAttr.test(attrs[key].nodeName))
  425. {
  426. var dataName = attrs[key].nodeName.match(reDataAttr)[1];
  427. var val = attrs[key].value;
  428. dataName = dataName.replace(/-([a-z])/g, replacer);
  429. if (this._isObjectString(val)) val = this._toObject(val);
  430. else val = (this._isNumber(val)) ? parseFloat(val) : this._getBooleanFromStr(val);
  431. data[dataName] = val;
  432. }
  433. }
  434. return data;
  435. }
  436. return this.attr(name, value, true);
  437. },
  438. val: function(value)
  439. {
  440. if (value === undefined)
  441. {
  442. var el = this.get();
  443. if (el.type && el.type === 'checkbox') return el.checked;
  444. else return el.value;
  445. }
  446. return this.each(function(node)
  447. {
  448. node.value = value;
  449. });
  450. },
  451. removeAttr: function(value)
  452. {
  453. return this.each(function(node)
  454. {
  455. var rmAttr = function(name) { if (node.nodeType !== 3) node.removeAttribute(name); };
  456. value.split(' ').forEach(rmAttr);
  457. });
  458. },
  459. removeData: function(value)
  460. {
  461. return this.each(function(node)
  462. {
  463. var rmData = function(name) { if (node.nodeType !== 3) node.removeAttribute('data-' + name); };
  464. value.split(' ').forEach(rmData);
  465. });
  466. },
  467. // dataset/dataget
  468. dataset: function(key, value)
  469. {
  470. return this.each(function(node)
  471. {
  472. DomCache[this.dataindex(node)][key] = value;
  473. });
  474. },
  475. dataget: function(key)
  476. {
  477. return DomCache[this.dataindex(this.get())][key];
  478. },
  479. dataindex: function(el)
  480. {
  481. var cacheIndex = el[DomExpando];
  482. var nextCacheIndex = DomCache.length;
  483. if (!cacheIndex)
  484. {
  485. cacheIndex = el[DomExpando] = nextCacheIndex;
  486. DomCache[cacheIndex] = {};
  487. }
  488. return cacheIndex;
  489. },
  490. // class
  491. addClass: function(value)
  492. {
  493. return this._eachClass(value, 'add');
  494. },
  495. removeClass: function(value)
  496. {
  497. return this._eachClass(value, 'remove');
  498. },
  499. toggleClass: function(value)
  500. {
  501. return this._eachClass(value, 'toggle');
  502. },
  503. hasClass: function(value)
  504. {
  505. return this.nodes.some(function(node)
  506. {
  507. return (node.classList) ? node.classList.contains(value) : false;
  508. });
  509. },
  510. // html & text
  511. empty: function()
  512. {
  513. return this.each(function(node)
  514. {
  515. node.innerHTML = '';
  516. });
  517. },
  518. html: function(html)
  519. {
  520. return (html === undefined) ? (this.get().innerHTML || '') : this.empty().append(html);
  521. },
  522. text: function(text)
  523. {
  524. return (text === undefined) ? (this.get().textContent || '') : this.each(function(node) { node.textContent = text; });
  525. },
  526. // manipulation
  527. after: function(html)
  528. {
  529. return this._inject(html, function(frag, node)
  530. {
  531. if (typeof frag === 'string')
  532. {
  533. node.insertAdjacentHTML('afterend', frag);
  534. }
  535. else
  536. {
  537. var elms = (frag instanceof Node) ? [frag] : this._toArray(frag).reverse();
  538. for (var i = 0; i < elms.length; i++)
  539. {
  540. node.parentNode.insertBefore(elms[i], node.nextSibling);
  541. }
  542. }
  543. return node;
  544. });
  545. },
  546. before: function(html)
  547. {
  548. return this._inject(html, function(frag, node)
  549. {
  550. if (typeof frag === 'string')
  551. {
  552. node.insertAdjacentHTML('beforebegin', frag);
  553. }
  554. else
  555. {
  556. var elms = (frag instanceof Node) ? [frag] : this._toArray(frag);
  557. for (var i = 0; i < elms.length; i++)
  558. {
  559. node.parentNode.insertBefore(elms[i], node);
  560. }
  561. }
  562. return node;
  563. });
  564. },
  565. append: function(html)
  566. {
  567. return this._inject(html, function(frag, node)
  568. {
  569. if (typeof frag === 'string' || typeof frag === 'number')
  570. {
  571. node.insertAdjacentHTML('beforeend', frag);
  572. }
  573. else
  574. {
  575. var elms = (frag instanceof Node) ? [frag] : this._toArray(frag);
  576. for (var i = 0; i < elms.length; i++)
  577. {
  578. node.appendChild(elms[i]);
  579. }
  580. }
  581. return node;
  582. });
  583. },
  584. prepend: function(html)
  585. {
  586. return this._inject(html, function(frag, node)
  587. {
  588. if (typeof frag === 'string' || typeof frag === 'number')
  589. {
  590. node.insertAdjacentHTML('afterbegin', frag);
  591. }
  592. else
  593. {
  594. var elms = (frag instanceof Node) ? [frag] : this._toArray(frag).reverse();
  595. for (var i = 0; i < elms.length; i++)
  596. {
  597. node.insertBefore(elms[i], node.firstChild);
  598. }
  599. }
  600. return node;
  601. });
  602. },
  603. wrap: function(html)
  604. {
  605. return this._inject(html, function(frag, node)
  606. {
  607. var wrapper = (typeof frag === 'string' || typeof frag === 'number') ? this.create(frag)[0] : (frag instanceof Node) ? frag : this._toArray(frag)[0];
  608. if (node.parentNode)
  609. {
  610. node.parentNode.insertBefore(wrapper, node);
  611. }
  612. wrapper.appendChild(node);
  613. return new Dom(wrapper);
  614. });
  615. },
  616. unwrap: function()
  617. {
  618. return this.each(function(node)
  619. {
  620. var $node = new Dom(node);
  621. return $node.replaceWith($node.contents());
  622. });
  623. },
  624. replaceWith: function(html)
  625. {
  626. return this._inject(html, function(frag, node)
  627. {
  628. var docFrag = document.createDocumentFragment();
  629. var elms = (typeof frag === 'string' || typeof frag === 'number') ? this.create(frag) : (frag instanceof Node) ? [frag] : this._toArray(frag);
  630. for (var i = 0; i < elms.length; i++)
  631. {
  632. docFrag.appendChild(elms[i]);
  633. }
  634. var result = docFrag.childNodes[0];
  635. node.parentNode.replaceChild(docFrag, node);
  636. return result;
  637. });
  638. },
  639. remove: function()
  640. {
  641. return this.each(function(node)
  642. {
  643. if (node.parentNode) node.parentNode.removeChild(node);
  644. });
  645. },
  646. clone: function(events)
  647. {
  648. var nodes = [];
  649. this.each(function(node)
  650. {
  651. var copy = this._clone(node);
  652. if (events) copy = this._cloneEvents(node, copy);
  653. nodes.push(copy);
  654. });
  655. return new Dom(nodes);
  656. },
  657. // show/hide
  658. show: function()
  659. {
  660. return this.each(function(node)
  661. {
  662. if (!node.style || !this._hasDisplayNone(node)) return;
  663. var target = node.getAttribute('domTargetShow');
  664. var isHidden = (node.classList) ? node.classList.contains(DomHClass) : false;
  665. var isHiddenMobile = (node.classList) ? node.classList.contains(DomHMClass) : false;
  666. var type;
  667. if (isHidden)
  668. {
  669. type = DomHClass;
  670. node.classList.remove(DomHClass);
  671. }
  672. else if (isHiddenMobile)
  673. {
  674. type = DomHMClass;
  675. node.classList.remove(DomHMClass);
  676. }
  677. else
  678. {
  679. node.style.display = (target) ? target : 'block';
  680. }
  681. if (type) node.setAttribute('domTargetHide', type);
  682. node.removeAttribute('domTargetShow');
  683. }.bind(this));
  684. },
  685. hide: function()
  686. {
  687. return this.each(function(node)
  688. {
  689. if (!node.style || this._hasDisplayNone(node)) return;
  690. var display = node.style.display;
  691. var target = node.getAttribute('domTargetHide');
  692. if (target === DomHClass)
  693. {
  694. node.classList.add(DomHClass);
  695. }
  696. else if (target === DomHMClass)
  697. {
  698. node.classList.add(DomHMClass);
  699. }
  700. else
  701. {
  702. if (display !== 'block') node.setAttribute('domTargetShow', display);
  703. node.style.display = 'none';
  704. }
  705. node.removeAttribute('domTargetHide');
  706. });
  707. },
  708. // dimensions
  709. scrollTop: function(value)
  710. {
  711. var node = this.get();
  712. var isWindow = (node === window);
  713. var isDocument = (node.nodeType === 9);
  714. var el = (isDocument) ? (document.scrollingElement || document.body.parentNode || document.body || document.documentElement) : node;
  715. if (value !== undefined)
  716. {
  717. if (isWindow) window.scrollTo(0, value);
  718. else el.scrollTop = value;
  719. return;
  720. }
  721. if (isDocument)
  722. {
  723. return (typeof window.pageYOffset != 'undefined') ? window.pageYOffset : ((document.documentElement.scrollTop) ? document.documentElement.scrollTop : ((document.body.scrollTop) ? document.body.scrollTop : 0));
  724. }
  725. else
  726. {
  727. return (isWindow) ? window.pageYOffset : el.scrollTop;
  728. }
  729. },
  730. offset: function()
  731. {
  732. return this._getDim('Offset');
  733. },
  734. position: function()
  735. {
  736. return this._getDim('Position');
  737. },
  738. width: function(value, adjust)
  739. {
  740. return this._getSize('width', 'Width', value, adjust);
  741. },
  742. height: function(value, adjust)
  743. {
  744. return this._getSize('height', 'Height', value, adjust);
  745. },
  746. outerWidth: function()
  747. {
  748. return this._getInnerOrOuter('width', 'outer');
  749. },
  750. outerHeight: function()
  751. {
  752. return this._getInnerOrOuter('height', 'outer');
  753. },
  754. innerWidth: function()
  755. {
  756. return this._getInnerOrOuter('width', 'inner');
  757. },
  758. innerHeight: function()
  759. {
  760. return this._getInnerOrOuter('height', 'inner');
  761. },
  762. // events
  763. click: function()
  764. {
  765. return this._triggerEvent('click');
  766. },
  767. focus: function()
  768. {
  769. return this._triggerEvent('focus');
  770. },
  771. trigger: function(names)
  772. {
  773. return this.each(function(node)
  774. {
  775. var events = names.split(' ');
  776. for (var i = 0; i < events.length; i++)
  777. {
  778. var ev;
  779. var opts = { bubbles: true, cancelable: true };
  780. try {
  781. ev = new window.CustomEvent(events[i], opts);
  782. } catch(e) {
  783. ev = document.createEvent('CustomEvent');
  784. ev.initCustomEvent(events[i], true, true);
  785. }
  786. node.dispatchEvent(ev);
  787. }
  788. });
  789. },
  790. on: function(names, handler, one)
  791. {
  792. return this.each(function(node)
  793. {
  794. var events = names.split(' ');
  795. for (var i = 0; i < events.length; i++)
  796. {
  797. var event = this._getEventName(events[i]);
  798. var namespace = this._getEventNamespace(events[i]);
  799. handler = (one) ? this._getOneHandler(handler, names) : handler;
  800. node.addEventListener(event, handler);
  801. node._e = node._e || {};
  802. node._e[namespace] = node._e[namespace] || {};
  803. node._e[namespace][event] = node._e[namespace][event] || [];
  804. node._e[namespace][event].push(handler);
  805. }
  806. });
  807. },
  808. one: function(events, handler)
  809. {
  810. return this.on(events, handler, true);
  811. },
  812. off: function(names, handler)
  813. {
  814. var testEvent = function(name, key, event) { return (name === event); };
  815. var testNamespace = function(name, key, event, namespace) { return (key === namespace); };
  816. var testEventNamespace = function(name, key, event, namespace) { return (name === event && key === namespace); };
  817. var testPositive = function() { return true; };
  818. if (names === undefined)
  819. {
  820. // ALL
  821. return this.each(function(node)
  822. {
  823. this._offEvent(node, false, false, handler, testPositive);
  824. });
  825. }
  826. return this.each(function(node)
  827. {
  828. var events = names.split(' ');
  829. for (var i = 0; i < events.length; i++)
  830. {
  831. var event = this._getEventName(events[i]);
  832. var namespace = this._getEventNamespace(events[i]);
  833. // 1) event without namespace
  834. if (namespace === '_events') this._offEvent(node, event, namespace, handler, testEvent);
  835. // 2) only namespace
  836. else if (!event && namespace !== '_events') this._offEvent(node, event, namespace, handler, testNamespace);
  837. // 3) event + namespace
  838. else this._offEvent(node, event, namespace, handler, testEventNamespace);
  839. }
  840. });
  841. },
  842. // form
  843. serialize: function(asObject)
  844. {
  845. var obj = {};
  846. var elms = this.get().elements;
  847. for (var i = 0; i < elms.length; i++)
  848. {
  849. var el = elms[i];
  850. if (/(checkbox|radio)/.test(el.type) && !el.checked) continue;
  851. if (!el.name || el.disabled || el.type === 'file') continue;
  852. if (el.type === 'select-multiple')
  853. {
  854. for (var z = 0; z < el.options.length; z++)
  855. {
  856. var opt = el.options[z];
  857. if (opt.selected) obj[el.name] = opt.value;
  858. }
  859. }
  860. obj[el.name] = (this._isNumber(el.value)) ? parseFloat(el.value) : this._getBooleanFromStr(el.value);
  861. }
  862. return (asObject) ? obj : this._toParams(obj);
  863. },
  864. ajax: function(success, error)
  865. {
  866. if (typeof AjaxRequest !== 'undefined')
  867. {
  868. var method = this.attr('method') || 'post';
  869. var options = {
  870. url: this.attr('action'),
  871. data: this.serialize(),
  872. success: success,
  873. error: error
  874. };
  875. return new AjaxRequest(method, options);
  876. }
  877. },
  878. // private
  879. _queryContext: function(selector, context)
  880. {
  881. context = this._getContext(context);
  882. return (context.nodeType !== 3 && typeof context.querySelectorAll === 'function') ? context.querySelectorAll(selector) : [];
  883. },
  884. _query: function(selector, context)
  885. {
  886. if (context)
  887. {
  888. return this._queryContext(selector, context);
  889. }
  890. else if (/^[.#]?[\w-]*$/.test(selector))
  891. {
  892. if (selector[0] === '#')
  893. {
  894. var element = document.getElementById(selector.slice(1));
  895. return element ? [element] : [];
  896. }
  897. if (selector[0] === '.')
  898. {
  899. return document.getElementsByClassName(selector.slice(1));
  900. }
  901. return document.getElementsByTagName(selector);
  902. }
  903. return document.querySelectorAll(selector);
  904. },
  905. _getContext: function(context)
  906. {
  907. context = (typeof context === 'string') ? document.querySelector(context) : context;
  908. return (context && context.dom) ? context.get() : (context || document);
  909. },
  910. _inject: function(html, fn)
  911. {
  912. var len = this.nodes.length;
  913. var nodes = [];
  914. while (len--)
  915. {
  916. var res = (typeof html === 'function') ? html.call(this, this.nodes[len]) : html;
  917. var el = (len === 0) ? res : this._clone(res);
  918. var node = fn.call(this, el, this.nodes[len]);
  919. if (node)
  920. {
  921. if (node.dom) nodes.push(node.get());
  922. else nodes.push(node);
  923. }
  924. }
  925. return new Dom(nodes);
  926. },
  927. _cloneEvents: function(node, copy)
  928. {
  929. var events = node._e;
  930. if (events)
  931. {
  932. copy._e = events;
  933. for (var name in events._events)
  934. {
  935. for (var i = 0; i < events._events[name].length; i++)
  936. {
  937. copy.addEventListener(name, events._events[name][i]);
  938. }
  939. }
  940. }
  941. return copy;
  942. },
  943. _clone: function(node)
  944. {
  945. if (typeof node === 'undefined') return;
  946. if (typeof node === 'string') return node;
  947. else if (node instanceof Node || node.nodeType) return node.cloneNode(true);
  948. else if ('length' in node)
  949. {
  950. return [].map.call(this._toArray(node), function(el) { return el.cloneNode(true); });
  951. }
  952. },
  953. _slice: function(obj)
  954. {
  955. return (!obj || obj.length === 0) ? [] : (obj.length) ? [].slice.call(obj.nodes || obj) : [obj];
  956. },
  957. _eachClass: function(value, type)
  958. {
  959. return this.each(function(node)
  960. {
  961. if (value)
  962. {
  963. var setClass = function(name) { if (node.classList) node.classList[type](name); };
  964. value.split(' ').forEach(setClass);
  965. }
  966. });
  967. },
  968. _triggerEvent: function(name)
  969. {
  970. var node = this.get();
  971. if (node && node.nodeType !== 3) node[name]();
  972. return this;
  973. },
  974. _getOneHandler: function(handler, events)
  975. {
  976. var self = this;
  977. return function()
  978. {
  979. handler.apply(this, arguments);
  980. self.off(events);
  981. };
  982. },
  983. _getEventNamespace: function(event)
  984. {
  985. var arr = event.split('.');
  986. var namespace = (arr[1]) ? arr[1] : '_events';
  987. return (arr[2]) ? namespace + arr[2] : namespace;
  988. },
  989. _getEventName: function(event)
  990. {
  991. return event.split('.')[0];
  992. },
  993. _offEvent: function(node, event, namespace, handler, condition)
  994. {
  995. for (var key in node._e)
  996. {
  997. for (var name in node._e[key])
  998. {
  999. if (condition(name, key, event, namespace))
  1000. {
  1001. var handlers = node._e[key][name];
  1002. for (var i = 0; i < handlers.length; i++)
  1003. {
  1004. if (typeof handler !== 'undefined' && handlers[i].toString() !== handler.toString())
  1005. {
  1006. continue;
  1007. }
  1008. node.removeEventListener(name, handlers[i]);
  1009. node._e[key][name].splice(i, 1);
  1010. if (node._e[key][name].length === 0) delete node._e[key][name];
  1011. if (Object.keys(node._e[key]).length === 0) delete node._e[key];
  1012. }
  1013. }
  1014. }
  1015. }
  1016. },
  1017. _getInnerOrOuter: function(method, type)
  1018. {
  1019. return this[method](undefined, type);
  1020. },
  1021. _getDocSize: function(node, type)
  1022. {
  1023. var body = node.body, html = node.documentElement;
  1024. return Math.max(body['scroll' + type], body['offset' + type], html['client' + type], html['scroll' + type], html['offset' + type]);
  1025. },
  1026. _getSize: function(type, captype, value, adjust)
  1027. {
  1028. if (value === undefined)
  1029. {
  1030. var el = this.get();
  1031. if (el.nodeType === 3) value = 0;
  1032. else if (el.nodeType === 9) value = this._getDocSize(el, captype);
  1033. else if (el === window) value = window['inner' + captype];
  1034. else value = this._getHeightOrWidth(type, el, adjust || 'normal');
  1035. return Math.round(value);
  1036. }
  1037. return this.each(function(node)
  1038. {
  1039. value = parseFloat(value);
  1040. value = value + this._adjustResultHeightOrWidth(type, node, adjust || 'normal');
  1041. new Dom(node).css(type, value + 'px');
  1042. }.bind(this));
  1043. },
  1044. _getHeightOrWidth: function(type, el, adjust)
  1045. {
  1046. if (!el) return 0;
  1047. var name = type.charAt(0).toUpperCase() + type.slice(1);
  1048. var result = 0;
  1049. var style = getComputedStyle(el, null);
  1050. var $el = new Dom(el);
  1051. var $targets = $el.parents().filter(function(node)
  1052. {
  1053. return (node.nodeType === 1 && getComputedStyle(node, null).display === 'none') ? node : false;
  1054. });
  1055. if (style.display === 'none') $targets.add(el);
  1056. if ($targets.length !== 0)
  1057. {
  1058. var fixStyle = 'visibility: hidden !important; display: block !important;';
  1059. var tmp = [];
  1060. $targets.each(function(node)
  1061. {
  1062. var $node = new Dom(node);
  1063. var thisStyle = $node.attr('style');
  1064. if (thisStyle !== null) tmp.push(thisStyle);
  1065. $node.attr('style', (thisStyle !== null) ? thisStyle + ';' + fixStyle : fixStyle);
  1066. });
  1067. result = $el.get()['offset' + name] - this._adjustResultHeightOrWidth(type, el, adjust);
  1068. $targets.each(function(node, i)
  1069. {
  1070. var $node = new Dom(node);
  1071. if (tmp[i] === undefined) $node.removeAttr('style');
  1072. else $node.attr('style', tmp[i]);
  1073. });
  1074. }
  1075. else
  1076. {
  1077. result = el['offset' + name] - this._adjustResultHeightOrWidth(type, el, adjust);
  1078. }
  1079. return result;
  1080. },
  1081. _adjustResultHeightOrWidth: function(type, el, adjust)
  1082. {
  1083. if (!el || adjust === false) return 0;
  1084. var fix = 0;
  1085. var style = getComputedStyle(el, null);
  1086. var isBorderBox = (style.boxSizing === "border-box");
  1087. if (type === 'height')
  1088. {
  1089. if (adjust === 'inner' || (adjust === 'normal' && isBorderBox))
  1090. {
  1091. fix += (parseFloat(style.borderTopWidth) || 0) + (parseFloat(style.borderBottomWidth) || 0);
  1092. }
  1093. if (adjust === 'outer') fix -= (parseFloat(style.marginTop) || 0) + (parseFloat(style.marginBottom) || 0);
  1094. }
  1095. else
  1096. {
  1097. if (adjust === 'inner' || (adjust === 'normal' && isBorderBox))
  1098. {
  1099. fix += (parseFloat(style.borderLeftWidth) || 0) + (parseFloat(style.borderRightWidth) || 0);
  1100. }
  1101. if (adjust === 'outer') fix -= (parseFloat(style.marginLeft) || 0) + (parseFloat(style.marginRight) || 0);
  1102. }
  1103. return fix;
  1104. },
  1105. _getDim: function(type)
  1106. {
  1107. var node = this.get();
  1108. return (node.nodeType === 3) ? { top: 0, left: 0 } : this['_get' + type](node);
  1109. },
  1110. _getPosition: function(node)
  1111. {
  1112. return { top: node.offsetTop, left: node.offsetLeft };
  1113. },
  1114. _getOffset: function(node)
  1115. {
  1116. var rect = node.getBoundingClientRect();
  1117. var doc = node.ownerDocument;
  1118. var docElem = doc.documentElement;
  1119. var win = doc.defaultView;
  1120. return {
  1121. top: rect.top + win.pageYOffset - docElem.clientTop,
  1122. left: rect.left + win.pageXOffset - docElem.clientLeft
  1123. };
  1124. },
  1125. _getSibling: function(selector, method)
  1126. {
  1127. selector = (selector && selector.dom) ? selector.get() : selector;
  1128. var isNode = (selector && selector.nodeType);
  1129. var sibling;
  1130. this.each(function(node)
  1131. {
  1132. while (node = node[method])
  1133. {
  1134. if ((isNode && node === selector) || new Dom(node).is(selector))
  1135. {
  1136. sibling = node;
  1137. return;
  1138. }
  1139. }
  1140. });
  1141. return new Dom(sibling);
  1142. },
  1143. _toArray: function(obj)
  1144. {
  1145. if (obj instanceof NodeList)
  1146. {
  1147. var arr = [];
  1148. for (var i = 0; i < obj.length; i++)
  1149. {
  1150. arr[i] = obj[i];
  1151. }
  1152. return arr;
  1153. }
  1154. else if (obj === undefined) return [];
  1155. else
  1156. {
  1157. return (obj.dom) ? obj.nodes : obj;
  1158. }
  1159. },
  1160. _toParams: function(obj)
  1161. {
  1162. var params = '';
  1163. for (var key in obj)
  1164. {
  1165. params += '&' + this._encodeUri(key) + '=' + this._encodeUri(obj[key]);
  1166. }
  1167. return params.replace(/^&/, '');
  1168. },
  1169. _toObject: function(str)
  1170. {
  1171. return (new Function("return " + str))();
  1172. },
  1173. _encodeUri: function(str)
  1174. {
  1175. return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+');
  1176. },
  1177. _isNumber: function(str)
  1178. {
  1179. return !isNaN(str) && !isNaN(parseFloat(str));
  1180. },
  1181. _isObjectString: function(str)
  1182. {
  1183. return (str.search(/^{/) !== -1);
  1184. },
  1185. _getBooleanFromStr: function(str)
  1186. {
  1187. if (str === 'true') return true;
  1188. else if (str === 'false') return false;
  1189. return str;
  1190. },
  1191. _hasDisplayNone: function(el)
  1192. {
  1193. return (el.style.display === 'none') || ((el.currentStyle) ? el.currentStyle.display : getComputedStyle(el, null).display) === 'none';
  1194. }
  1195. };
  1196. // Wrapper
  1197. var $K = {};
  1198. // Globals
  1199. $K.app = [];
  1200. $K.version = '7.2.1';
  1201. $K.options = {};
  1202. $K.modules = {};
  1203. $K.services = {};
  1204. $K.plugins = {};
  1205. $K.classes = {};
  1206. $K.extends = {};
  1207. $K.lang = {};
  1208. $K.dom = function(selector, context) { return new Dom(selector, context); };
  1209. $K.ajax = Ajax;
  1210. $K.Dom = Dom;
  1211. $K.env = {
  1212. 'module': 'modules',
  1213. 'service': 'services',
  1214. 'plugin': 'plugins',
  1215. 'class': 'classes',
  1216. 'extend': 'extends'
  1217. };
  1218. // init class
  1219. var KubeApp = function(options, args)
  1220. {
  1221. return ($K.app = new App(options));
  1222. };
  1223. // init
  1224. $K.init = function(options)
  1225. {
  1226. return new KubeApp(options, [].slice.call(arguments, 1));
  1227. };
  1228. // api
  1229. $K.api = function(name)
  1230. {
  1231. var app = $K.app;
  1232. var args = [].slice.call(arguments, 1);
  1233. if (app)
  1234. {
  1235. args.unshift(name);
  1236. app.api.apply(app, args);
  1237. }
  1238. };
  1239. // add
  1240. $K.add = function(type, name, obj)
  1241. {
  1242. if (typeof $K.env[type] === 'undefined') return;
  1243. // translations
  1244. if (obj.translations)
  1245. {
  1246. $K.lang = $K.extend(true, {}, $K.lang, obj.translations);
  1247. }
  1248. // extend
  1249. if (type === 'extend')
  1250. {
  1251. $K[$K.env[type]][name] = obj;
  1252. }
  1253. else
  1254. {
  1255. // prototype
  1256. var F = function() {};
  1257. F.prototype = obj;
  1258. // extends
  1259. if (obj.extends)
  1260. {
  1261. for (var i = 0; i < obj.extends.length; i++)
  1262. {
  1263. $K.inherit(F, $K.extends[obj.extends[i]]);
  1264. }
  1265. }
  1266. $K[$K.env[type]][name] = F;
  1267. }
  1268. };
  1269. // add lang
  1270. $K.addLang = function(lang, obj)
  1271. {
  1272. if (typeof $K.lang[lang] === 'undefined')
  1273. {
  1274. $K.lang[lang] = {};
  1275. }
  1276. $K.lang[lang] = $K.extend($K.lang[lang], obj);
  1277. };
  1278. // create
  1279. $K.create = function(name)
  1280. {
  1281. var arr = name.split('.');
  1282. var args = [].slice.call(arguments, 1);
  1283. var type = 'classes';
  1284. if (typeof $K.env[arr[0]] !== 'undefined')
  1285. {
  1286. type = $K.env[arr[0]];
  1287. name = arr.slice(1).join('.');
  1288. }
  1289. // construct
  1290. var instance = new $K[type][name]();
  1291. instance._type = arr[0];
  1292. instance._name = name;
  1293. // init
  1294. if (instance.init)
  1295. {
  1296. var res = instance.init.apply(instance, args);
  1297. return (res) ? res : instance;
  1298. }
  1299. return instance;
  1300. };
  1301. // inherit
  1302. $K.inherit = function(current, parent)
  1303. {
  1304. var F = function () {};
  1305. F.prototype = parent;
  1306. var f = new F();
  1307. for (var prop in current.prototype)
  1308. {
  1309. if (current.prototype.__lookupGetter__(prop)) f.__defineGetter__(prop, current.prototype.__lookupGetter__(prop));
  1310. else f[prop] = current.prototype[prop];
  1311. }
  1312. current.prototype = f;
  1313. current.prototype.super = parent;
  1314. return current;
  1315. };
  1316. // error
  1317. $K.error = function(exception)
  1318. {
  1319. throw exception;
  1320. };
  1321. // extend
  1322. $K.extend = function()
  1323. {
  1324. var extended = {};
  1325. var deep = false;
  1326. var i = 0;
  1327. var length = arguments.length;
  1328. if (Object.prototype.toString.call( arguments[0] ) === '[object Boolean]')
  1329. {
  1330. deep = arguments[0];
  1331. i++;
  1332. }
  1333. var merge = function(obj)
  1334. {
  1335. for (var prop in obj)
  1336. {
  1337. if (Object.prototype.hasOwnProperty.call(obj, prop))
  1338. {
  1339. if (deep && Object.prototype.toString.call(obj[prop]) === '[object Object]') extended[prop] = $K.extend(true, extended[prop], obj[prop]);
  1340. else extended[prop] = obj[prop];
  1341. }
  1342. }
  1343. };
  1344. for (; i < length; i++ )
  1345. {
  1346. var obj = arguments[i];
  1347. merge(obj);
  1348. }
  1349. return extended;
  1350. };
  1351. var App = function(options)
  1352. {
  1353. this.modules = {};
  1354. this.services = [];
  1355. this.queueStart = { 'service': {}, 'module': {} };
  1356. this.queueStop = { 'service': {}, 'module': {} };
  1357. this.started = false;
  1358. this.stopped = false;
  1359. // environment
  1360. this.namespace = 'kube';
  1361. this.dataNamespace = 'data-kube';
  1362. this.instancePrefix = 'kube-instance-';
  1363. this.rootOpts = options;
  1364. this.$win = $K.dom(window);
  1365. this.$doc = $K.dom(document);
  1366. this.$body = $K.dom('body');
  1367. // core services
  1368. this.coreServices = ['options', 'lang', 'utils'];
  1369. this.bindableServices = ['opts', 'lang', 'utils', '$win', '$doc', '$body']
  1370. this.utils = $K.create('service.utils', this);
  1371. this.opts = $K.create('service.options', this, 'global', options);
  1372. this.lang = $K.create('service.lang', this);
  1373. this.appcallback = new App.Callback(this);
  1374. this.appstarter = new App.Starter(this);
  1375. this.appbuilder = new App.Builder(this);
  1376. this.appbroadcast = new App.Broadcast(this);
  1377. this.appapi = new App.Api(this);
  1378. this.build();
  1379. this.start();
  1380. };
  1381. App.prototype = {
  1382. // build
  1383. build: function()
  1384. {
  1385. this.appbuilder.build();
  1386. },
  1387. // start & stop
  1388. start: function()
  1389. {
  1390. // start
  1391. this.stopped = false;
  1392. this.broadcast('start', this);
  1393. // starter
  1394. this.appstarter.start();
  1395. // started
  1396. this.broadcast('started', this);
  1397. this.started = true;
  1398. },
  1399. stop: function()
  1400. {
  1401. this.started = false;
  1402. this.stopped = true;
  1403. // stop
  1404. this.broadcast('stop', this);
  1405. // stopper
  1406. this.appstarter.stop();
  1407. // stopped
  1408. this.broadcast('stopped', this);
  1409. },
  1410. // starter & stopper
  1411. starter: function(instance, priority)
  1412. {
  1413. var type = (instance._type !== 'service') ? 'module' : instance._type;
  1414. this.queueStart[type][priority] = instance._name;
  1415. },
  1416. stopper: function(instance, priority)
  1417. {
  1418. var type = (instance._type !== 'service') ? 'module' : instance._type;
  1419. this.queueStop[type][priority] = instance._name;
  1420. },
  1421. // started & stopped
  1422. isStarted: function()
  1423. {
  1424. return this.started;
  1425. },
  1426. isStopped: function()
  1427. {
  1428. return this.stopped;
  1429. },
  1430. // broadcast
  1431. broadcast: function(name, sender)
  1432. {
  1433. this.appbroadcast.trigger(name, sender, [].slice.call(arguments, 2));
  1434. },
  1435. // callback
  1436. on: function(name, func)
  1437. {
  1438. this.appcallback.add(name, func);
  1439. },
  1440. off: function(name, func)
  1441. {
  1442. this.appcallback.remove(name, func);
  1443. },
  1444. // api
  1445. api: function(name)
  1446. {
  1447. return this.appapi.trigger(name, [].slice.call(arguments, 1));
  1448. }
  1449. };
  1450. App.Module = function(app, $el, name, id)
  1451. {
  1452. this.app = app;
  1453. this.instancePrefix = app.instancePrefix;
  1454. // local
  1455. this.eventTypes = ['click', 'mouseover', 'mouseout', 'mousedown', 'mouseup', 'mousemove',
  1456. 'keydown', 'keyup', 'focus', 'submit', 'change', 'contextmenu', 'input'];
  1457. // build
  1458. return this._build($el, name, id);
  1459. };
  1460. App.Module.prototype = {
  1461. _build: function($el, name, id)
  1462. {
  1463. var instance = $el.dataget(this.instancePrefix + name);
  1464. if (!instance && typeof $K.modules[name] !== 'undefined')
  1465. {
  1466. var context = new App.Context(this.app, $el, id);
  1467. var $target = context.getTarget();
  1468. instance = $K.create('module.' + name, this.app, context);
  1469. instance._id = id;
  1470. $el.dataset(this.instancePrefix + name, instance);
  1471. $el.attr('data-loaded', true);
  1472. // delegate events
  1473. this._delegateModuleEvents(instance, $el, name);
  1474. // delegate commands
  1475. this._delegateModuleCommands(instance, $el);
  1476. if ($target.is())
  1477. {
  1478. this._delegateModuleCommands(instance, $target);
  1479. }
  1480. }
  1481. return instance;
  1482. },
  1483. _delegateModuleCommands: function(instance, $el)
  1484. {
  1485. $el.find('[data-command]').each(function(node)
  1486. {
  1487. this._delegateCommand(instance, node, node.getAttribute('data-command'));
  1488. }.bind(this));
  1489. },
  1490. _delegateCommand: function(instance, node, command)
  1491. {
  1492. if (typeof instance._eventCommands === 'undefined') instance._eventCommands = [];
  1493. var self = this;
  1494. var $node = $K.dom(node);
  1495. instance._eventCommands.push($node);
  1496. $node.on('click.generatedcommand', function(e)
  1497. {
  1498. e.preventDefault();
  1499. var args = $node.data();
  1500. args.event = e;
  1501. self.app.broadcast(command, instance, $node, args);
  1502. });
  1503. },
  1504. _delegateModuleEvents: function(instance, $el, name)
  1505. {
  1506. $el.find('[data-type]').each(function(node)
  1507. {
  1508. var arr = node.getAttribute('data-type').split('.');
  1509. var type = arr[0];
  1510. var scope = name;
  1511. if (arr.length === 2)
  1512. {
  1513. scope = arr[0];
  1514. type = arr[1];
  1515. }
  1516. if (scope === name)
  1517. {
  1518. this._delegateEvent(instance, name, node, type);
  1519. }
  1520. }.bind(this));
  1521. },
  1522. _delegateEvent: function(instance, name, node, type)
  1523. {
  1524. if (typeof instance._eventNodes === 'undefined') instance._eventNodes = [];
  1525. var $node = $K.dom(node);
  1526. var callback = function(e, eventType, element, type, args)
  1527. {
  1528. return instance['on' + eventType].call(instance, e, element, type, args);
  1529. };
  1530. instance._eventNodes.push($node);
  1531. for (var i = 0; i < this.eventTypes.length; i++)
  1532. {
  1533. var event = 'on' + this.eventTypes[i];
  1534. if (typeof instance[event] === 'function')
  1535. {
  1536. $node.on(this.eventTypes[i] + '.generatedevent', function(e)
  1537. {
  1538. var args = $node.data();
  1539. callback(e, e.type, this, type, args);
  1540. });
  1541. }
  1542. }
  1543. }
  1544. };
  1545. App.Context = function(app, $el, name)
  1546. {
  1547. this.app = app;
  1548. this.opts = app.opts;
  1549. // build
  1550. this.$element = this._buildElement($el);
  1551. this.params = this._buildParams();
  1552. this.name = this._buildName(name);
  1553. this.$target = this._buildTarget();
  1554. };
  1555. App.Context.prototype = {
  1556. // public
  1557. getElement: function()
  1558. {
  1559. return this.$element;
  1560. },
  1561. getTarget: function()
  1562. {
  1563. return this.$target;
  1564. },
  1565. getParams: function(defaults)
  1566. {
  1567. return (defaults) ? $K.extend({}, defaults, this.params) : this.params;
  1568. },
  1569. getName: function()
  1570. {
  1571. return this.name;
  1572. },
  1573. // private
  1574. _buildName: function(name)
  1575. {
  1576. return (this.params.name) ? this.params.name : name;
  1577. },
  1578. _buildParams: function()
  1579. {
  1580. return $K.create('service.options', this.app, 'element', this.$element);
  1581. },
  1582. _buildElement: function($el)
  1583. {
  1584. return new App.Element(this.app, $el);
  1585. },
  1586. _buildTarget: function()
  1587. {
  1588. return new App.Target(this.app, this.params.target);
  1589. }
  1590. };
  1591. App.Callback = function(app)
  1592. {
  1593. this.app = app;
  1594. this.opts = app.opts;
  1595. // local
  1596. this.callbacks = {};
  1597. // build
  1598. this._build();
  1599. };
  1600. App.Callback.prototype = {
  1601. stop: function()
  1602. {
  1603. this.callbacks = {};
  1604. },
  1605. add: function(name, handler)
  1606. {
  1607. if (typeof this.callbacks[name] === 'undefined') this.callbacks[name] = [];
  1608. this.callbacks[name].push(handler);
  1609. },
  1610. remove: function(name, handler)
  1611. {
  1612. if (handler === undefined)
  1613. {
  1614. delete this.callbacks[name];
  1615. }
  1616. else
  1617. {
  1618. for (var i = 0; i < this.callbacks[name].length; i++)
  1619. {
  1620. this.callbacks[name].splice(i, 1);
  1621. }
  1622. if (this.callbacks[name].length === 0)
  1623. {
  1624. delete this.callbacks[name];
  1625. }
  1626. }
  1627. },
  1628. trigger: function(name, args)
  1629. {
  1630. if (typeof this.callbacks[name] === 'undefined') return;
  1631. for (var i = 0; i < this.callbacks[name].length; i++)
  1632. {
  1633. this.callbacks[name][i].apply(this.app, args);
  1634. }
  1635. },
  1636. // private
  1637. _build: function()
  1638. {
  1639. if (this.opts.callbacks)
  1640. {
  1641. for (var name in this.opts.callbacks)
  1642. {
  1643. if (typeof this.opts.callbacks[name] === 'function')
  1644. {
  1645. if (typeof this.callbacks[name] === 'undefined') this.callbacks[name] = [];
  1646. this.callbacks[name].push(this.opts.callbacks[name]);
  1647. }
  1648. else
  1649. {
  1650. for (var key in this.opts.callbacks[name])
  1651. {
  1652. if (typeof this.callbacks[name + '.' + key] === 'undefined') this.callbacks[name + '.' + key] = [];
  1653. this.callbacks[name + '.' + key].push(this.opts.callbacks[name][key]);
  1654. }
  1655. }
  1656. }
  1657. }
  1658. }
  1659. };
  1660. App.Element = function(app, $el)
  1661. {
  1662. this.app = app;
  1663. this.parse($el);
  1664. };
  1665. App.Element.prototype = {
  1666. isOpened: function()
  1667. {
  1668. return !this.isClosed();
  1669. },
  1670. isClosed: function()
  1671. {
  1672. return (this.hasClass('is-hidden') || this.css('display') === 'none');
  1673. }
  1674. };
  1675. $K.inherit(App.Element, Dom.prototype);
  1676. App.Target = function(app, selector)
  1677. {
  1678. this.app = app;
  1679. this.parse(selector);
  1680. };
  1681. App.Target.prototype = {
  1682. isOpened: function()
  1683. {
  1684. return !this.isClosed();
  1685. },
  1686. isClosed: function()
  1687. {
  1688. var self = this;
  1689. var count = 0;
  1690. var len = this.length;
  1691. this.each(function(node)
  1692. {
  1693. var $node = $K.dom(node);
  1694. if ($node.hasClass('is-hidden') || $node.css('display') === 'none')
  1695. {
  1696. count++;
  1697. }
  1698. });
  1699. return (count === len);
  1700. }
  1701. };
  1702. $K.inherit(App.Target, Dom.prototype);
  1703. App.Api = function(app)
  1704. {
  1705. this.app = app;
  1706. this.modules = app.modules;
  1707. };
  1708. App.Api.prototype = {
  1709. trigger: function(name, args)
  1710. {
  1711. var arr = name.split('.');
  1712. var isNamed = (arr.length === 3);
  1713. var isApp = (arr.length === 1);
  1714. var isCallback = (arr[0] === 'on' || arr[0] === 'off');
  1715. var module = arr[0];
  1716. var method = arr[1];
  1717. var id = false;
  1718. if (isApp)
  1719. {
  1720. module = false;
  1721. method = arr[0];
  1722. }
  1723. else if (isNamed)
  1724. {
  1725. method = arr[2];
  1726. id = arr[1];
  1727. }
  1728. // app
  1729. if (isApp)
  1730. {
  1731. if (typeof this.app[method] === 'function')
  1732. {
  1733. return this._call(this.app, method, args);
  1734. }
  1735. }
  1736. // callback
  1737. else if (isCallback)
  1738. {
  1739. return (module === 'on') ? this.app.on(module, args[0]) : this.app.off(module, args[0] || undefined);
  1740. }
  1741. else
  1742. {
  1743. // service
  1744. if (this._isInstanceExists(this.app, module))
  1745. {
  1746. return this._call(this.app[module], method, args);
  1747. }
  1748. // module / plugin / addon
  1749. else if (this._isInstanceExists(this.modules, module))
  1750. {
  1751. this._doApi(module, method, id, args)
  1752. }
  1753. }
  1754. },
  1755. // private
  1756. _isInstanceExists: function(obj, name)
  1757. {
  1758. return (typeof obj[name] !== 'undefined');
  1759. },
  1760. _doApi: function(module, method, id, args)
  1761. {
  1762. for (var key in this.modules[module])
  1763. {
  1764. if (id === false || id === key)
  1765. {
  1766. var instance = this.modules[module][key];
  1767. this._call(instance, method, args);
  1768. }
  1769. }
  1770. },
  1771. _call: function(instance, method, args)
  1772. {
  1773. if (typeof instance[method] === 'function')
  1774. {
  1775. return instance[method].apply(instance, args);
  1776. }
  1777. }
  1778. };
  1779. App.Broadcast = function(app)
  1780. {
  1781. this.app = app;
  1782. this.modules = app.modules;
  1783. this.callback = app.appcallback;
  1784. };
  1785. App.Broadcast.prototype = {
  1786. trigger: function(name, sender, args)
  1787. {
  1788. if (Array.isArray(name))
  1789. {
  1790. sender._id = name[0];
  1791. name = name[1];
  1792. }
  1793. else if (sender && typeof sender.context !== 'undefined')
  1794. {
  1795. sender._id = sender.context.getName();
  1796. }
  1797. args.unshift(sender);
  1798. for (var moduleName in this.modules)
  1799. {
  1800. for (var key in this.modules[moduleName])
  1801. {
  1802. var instance = this.modules[moduleName][key];
  1803. this._call(instance, name, args, sender);
  1804. }
  1805. }
  1806. this.callback.trigger(name, args);
  1807. },
  1808. // private
  1809. _call: function(instance, name, args, sender)
  1810. {
  1811. // new
  1812. if (typeof instance['onmessage'] !== 'undefined')
  1813. {
  1814. var arr = name.split('.');
  1815. var func = instance['onmessage'][arr[0]];
  1816. if (arr.length === 1 && typeof func === 'function')
  1817. {
  1818. func.apply(instance, args);
  1819. }
  1820. else if (arr.length === 2 && typeof func !== 'undefined' && typeof func[arr[1]] === 'function')
  1821. {
  1822. func[arr[1]].apply(instance, args);
  1823. }
  1824. }
  1825. // 7.1.1 compatibility
  1826. var arr = name.split('.');
  1827. if (arr.length === 1)
  1828. {
  1829. if (typeof instance['on' + name] === 'function')
  1830. {
  1831. instance['on' + name].apply(instance, args);
  1832. }
  1833. }
  1834. else
  1835. {
  1836. arr[0] = 'on' + arr[0];
  1837. // without id
  1838. var func = this.app.utils.checkProperty(instance, arr);
  1839. if (typeof func === 'function')
  1840. {
  1841. func.apply(instance, args);
  1842. }
  1843. // with id
  1844. if (sender && sender._id)
  1845. {
  1846. var idArr = [arr[0], sender._id, arr[1]];
  1847. var func = this.app.utils.checkProperty(instance, idArr);
  1848. if (typeof func === 'function')
  1849. {
  1850. func.apply(instance, args);
  1851. }
  1852. }
  1853. }
  1854. }
  1855. };
  1856. App.Builder = function(app)
  1857. {
  1858. this.app = app;
  1859. this.opts = app.opts;
  1860. this.$doc = app.$doc;
  1861. this.dataNamespace = app.dataNamespace;
  1862. };
  1863. App.Builder.prototype = {
  1864. build: function()
  1865. {
  1866. this._buildServices();
  1867. this._buildModules();
  1868. },
  1869. // private
  1870. _buildServices: function()
  1871. {
  1872. var services = [];
  1873. var startableServices = [];
  1874. for (var name in $K.services)
  1875. {
  1876. if (this.app.coreServices.indexOf(name) === -1)
  1877. {
  1878. this.app[name] = $K.create('service.' + name, this.app);
  1879. this.app.bindableServices.push(name);
  1880. services.push(name);
  1881. startableServices.push(name);
  1882. }
  1883. }
  1884. // make core services to use another services
  1885. for (var i = 0; i < this.app.coreServices.length; i++)
  1886. {
  1887. var name = this.app.coreServices[i];
  1888. if (name !== 'options') services.push(name);
  1889. }
  1890. // binding
  1891. for (var i = 0; i < services.length; i++)
  1892. {
  1893. var service = services[i];
  1894. for (var z = 0; z < this.app.bindableServices.length; z++)
  1895. {
  1896. var inj = this.app.bindableServices[z];
  1897. if (service !== inj)
  1898. {
  1899. this.app[service][inj] = this.app[inj];
  1900. }
  1901. }
  1902. }
  1903. this.app.services = startableServices;
  1904. },
  1905. _buildModules: function()
  1906. {
  1907. this.$doc.find('[' + this.dataNamespace + ']').each(function(node, i)
  1908. {
  1909. var $el = $K.dom(node);
  1910. var name = $el.attr(this.dataNamespace);
  1911. var id = ($el.attr('id')) ? $el.attr('id') : name + '-' + i;
  1912. id = ($el.attr('data-name')) ? $el.attr('data-name') : id;
  1913. var instance = new App.Module(this.app, $el, name, id);
  1914. this._storeElementModule(instance, name, id);
  1915. }.bind(this));
  1916. },
  1917. _storeElementModule: function(instance, name, id)
  1918. {
  1919. if (instance)
  1920. {
  1921. if (typeof this.app.modules[name] === 'undefined')
  1922. {
  1923. this.app.modules[name] = {};
  1924. }
  1925. this.app.modules[name][id] = instance;
  1926. }
  1927. }
  1928. };
  1929. App.Starter = function(app)
  1930. {
  1931. this.app = app;
  1932. this.queue = {
  1933. 'start': app.queueStart,
  1934. 'stop': app.queueStop
  1935. };
  1936. this.priority = {
  1937. 'start': { 'service': [], 'module': [] },
  1938. 'stop': { 'service': [], 'module': [] }
  1939. };
  1940. };
  1941. App.Starter.prototype = {
  1942. start: function()
  1943. {
  1944. this._stopStart('service', 'start');
  1945. this._stopStart('module', 'start');
  1946. },
  1947. stop: function()
  1948. {
  1949. this._stopStart('service', 'stop');
  1950. this._stopStart('module', 'stop');
  1951. },
  1952. // private
  1953. _stopStart: function(type, method)
  1954. {
  1955. // priority
  1956. var queue = this.queue[method][type];
  1957. for (var key in queue)
  1958. {
  1959. var name = queue[key];
  1960. var instance = (type === 'service') ? this.app[name] : this.app.modules[name];
  1961. this._callInstances(type, method, instance);
  1962. this.priority[method][type].push(name);
  1963. }
  1964. // common
  1965. var modules = (type === 'service') ? this.app.services : this.app.modules;
  1966. for (var key in modules)
  1967. {
  1968. var name = (type === 'service') ? modules[key] : key;
  1969. if (this.priority[method][type].indexOf(name) === -1)
  1970. {
  1971. var instance = (type === 'service') ? this.app[name] : modules[name];
  1972. this._callInstances(type, method, instance);
  1973. }
  1974. }
  1975. },
  1976. _stopModuleEvents: function(method, instance)
  1977. {
  1978. if (method === 'stop')
  1979. {
  1980. if (typeof instance._eventNodes !== 'undefined')
  1981. {
  1982. for (var i = 0; i < instance._eventNodes.length; i++)
  1983. {
  1984. instance._eventNodes[i].off('.generatedevent');
  1985. }
  1986. }
  1987. if (typeof instance._eventCommands !== 'undefined')
  1988. {
  1989. for (var i = 0; i < instance._eventCommands.length; i++)
  1990. {
  1991. instance._eventCommands[i].off('.generatedcommand');
  1992. }
  1993. }
  1994. }
  1995. },
  1996. _callInstances: function(type, method, instance)
  1997. {
  1998. if (type === 'service')
  1999. {
  2000. this._call(instance, method);
  2001. }
  2002. else
  2003. {
  2004. for (var key in instance)
  2005. {
  2006. this._call(instance[key], method);
  2007. this._stopModuleEvents(method, instance[key]);
  2008. }
  2009. }
  2010. },
  2011. _call: function(instance, method, args)
  2012. {
  2013. if (typeof instance[method] === 'function')
  2014. {
  2015. return instance[method].apply(instance, args);
  2016. }
  2017. }
  2018. };
  2019. $K.add('extend', 'dom', $K.Dom.prototype);
  2020. $K.add('service', 'animate', {
  2021. init: function(app)
  2022. {
  2023. this.app = app;
  2024. // local
  2025. this.animationOpt = true;
  2026. },
  2027. run: function(element, animation, callback)
  2028. {
  2029. return new $K.AnimatePlay(this.app, element, animation, callback, this.animationOpt);
  2030. },
  2031. remove: function(element)
  2032. {
  2033. this.$el = $K.dom(element);
  2034. var effect = this.$el.attr('kube-animate-effect');
  2035. this.$el.hide();
  2036. this.$el.removeClass(effect);
  2037. this.$el.off('animationend webkitAnimationEnd');
  2038. }
  2039. });
  2040. $K.AnimatePlay = function(app, element, animation, callback, animationOpt)
  2041. {
  2042. this.hidableEffects = ['fadeOut', 'flipOut', 'slideUp', 'zoomOut', 'slideOutUp', 'slideOutRight', 'slideOutLeft'];
  2043. this.prefix = 'kube-';
  2044. this.prefixes = ['', '-webkit-'];
  2045. this.utils = app.utils;
  2046. this.$el = $K.dom(element);
  2047. this.$body = $K.dom('body');
  2048. this.callback = callback;
  2049. this.animation = (!animationOpt) ? this._buildAnimationOff(animation) : animation;
  2050. this._setHeight();
  2051. // animate
  2052. if (this._isAnimate()) this._animate();
  2053. else this._toggle();
  2054. };
  2055. $K.AnimatePlay.prototype = {
  2056. _setHeight: function()
  2057. {
  2058. if (this.animation === 'slideUp' || this.animation === 'slideDown')
  2059. {
  2060. this.$el.height(this.$el.height());
  2061. }
  2062. },
  2063. _buildAnimationOff: function(animation)
  2064. {
  2065. return (this._isHidable(animation)) ? 'hide' : 'show';
  2066. },
  2067. _isAnimate: function()
  2068. {
  2069. return (this.animation !== 'show' && this.animation !== 'hide');
  2070. },
  2071. _isHidable: function(effect)
  2072. {
  2073. return (this.hidableEffects.indexOf(effect) !== -1);
  2074. },
  2075. _clean: function()
  2076. {
  2077. this.$body.removeClass('is-no-scroll-x');
  2078. this.$el.removeClass(this.prefix + this.animation);
  2079. this.$el.removeAttr('kube-animate-effect');
  2080. },
  2081. _toggle: function()
  2082. {
  2083. if (this.animation === 'show') this.$el.show();
  2084. else this.$el.hide();
  2085. if (typeof this.callback === 'function') this.callback(this);
  2086. },
  2087. _animate: function()
  2088. {
  2089. this.$body.addClass('is-no-scroll-x');
  2090. this.$el.show();
  2091. this.$el.addClass(this.prefix + this.animation);
  2092. this.$el.attr('kube-animate-effect', this.prefix + this.animation);
  2093. this._complete();
  2094. },
  2095. _complete: function()
  2096. {
  2097. this.$el.one('animationend webkitAnimationEnd', function(e)
  2098. {
  2099. if (this.$el.hasClass(this.prefix + this.animation)) this._clean();
  2100. if (this._isHidable(this.animation)) this.$el.hide();
  2101. if (this.animation === 'slideUp' || this.animation === 'slideDown') this.$el.css('height', '');
  2102. if (typeof this.callback === 'function') this.callback(this.$el);
  2103. }.bind(this));
  2104. }
  2105. };
  2106. $K.add('service', 'transition', {
  2107. init: function(app)
  2108. {
  2109. this.transitionOpt = true;
  2110. },
  2111. run: function(element, params)
  2112. {
  2113. return new $K.TransitionPlay(params, element, this.transitionOpt);
  2114. },
  2115. remove: function(element)
  2116. {
  2117. this.$el = $K.dom(element);
  2118. var classname = this.$el.attr('kube-transition-class');
  2119. if (classname)
  2120. {
  2121. this.$el.removeClass(classname);
  2122. this.$el.removeAttr('kube-transition-class');
  2123. }
  2124. var css = this.$el.attr('kube-transition-css');
  2125. if (css)
  2126. {
  2127. var names = css.split(',');
  2128. for (var i = 0; i < names.length; i++)
  2129. {
  2130. this.$el.css(names[i], '');
  2131. }
  2132. this.$el.removeAttr('kube-transition-css');
  2133. }
  2134. this.$el.off('transitionend webkitTransitionEnd');
  2135. }
  2136. });
  2137. $K.TransitionPlay = function(params, element, transitionOpt)
  2138. {
  2139. this.$el = $K.dom(element);
  2140. this.params = params;
  2141. this._transition();
  2142. };
  2143. $K.TransitionPlay.prototype = {
  2144. _transition: function()
  2145. {
  2146. if (this.params.classname)
  2147. {
  2148. this.$el.addClass(this.params.classname);
  2149. this.$el.attr('kube-transition-class', this.params.classname);
  2150. }
  2151. if (this.params.css)
  2152. {
  2153. this.$el.css(this.params.css);
  2154. var names = [];
  2155. for (var key in this.params.css)
  2156. {
  2157. names.push(key);
  2158. }
  2159. this.$el.attr('kube-transition-css', names.join(','))
  2160. }
  2161. this._complete();
  2162. },
  2163. _complete: function()
  2164. {
  2165. this.$el.one('transitionend webkitTransitionEnd', function(e)
  2166. {
  2167. if (typeof this.params.callback === 'function') this.params.callback(this.$el);
  2168. }.bind(this));
  2169. }
  2170. };
  2171. $K.add('service', 'lang', {
  2172. init: function(app)
  2173. {
  2174. this.app = app;
  2175. this.opts = app.opts;
  2176. var lang = (this.opts.lang) ? this.opts.lang : 'en';
  2177. // build
  2178. this.vars = this.build(lang);
  2179. },
  2180. build: function(lang)
  2181. {
  2182. lang = ($K.lang[lang] === undefined) ? 'en' : lang;
  2183. return ($K.lang[lang] !== undefined) ? $K.lang[lang] : [];
  2184. },
  2185. rebuild: function(lang)
  2186. {
  2187. this.opts.lang = lang;
  2188. this.vars = this.build(lang);
  2189. },
  2190. extend: function(obj)
  2191. {
  2192. this.vars = $K.extend(this.vars, obj);
  2193. },
  2194. parse: function(str)
  2195. {
  2196. if (str === undefined)
  2197. {
  2198. return '';
  2199. }
  2200. var matches = str.match(/## (.*?) ##/g);
  2201. if (matches)
  2202. {
  2203. for (var i = 0; i < matches.length; i++)
  2204. {
  2205. var key = matches[i].replace(/^##\s/g, '').replace(/\s##$/g, '');
  2206. str = str.replace(matches[i], this.get(key));
  2207. }
  2208. }
  2209. return str;
  2210. },
  2211. get: function(name)
  2212. {
  2213. return (typeof this.vars[name] !== 'undefined') ? this.vars[name] : '';
  2214. }
  2215. });
  2216. $K.add('service', 'options', {
  2217. init: function(app, type, opts)
  2218. {
  2219. this.app = app;
  2220. this.utils = app.utils;
  2221. return (type === 'global') ? this._build(opts) : this._buildElement(opts);
  2222. },
  2223. _build: function(opts)
  2224. {
  2225. return (opts) ? this._extendFromElements(opts) : {};
  2226. },
  2227. _buildElement: function($el)
  2228. {
  2229. return $K.extend(
  2230. {},
  2231. $el.data()
  2232. );
  2233. },
  2234. _extendFromElements: function(options)
  2235. {
  2236. return (options.hasOwnProperty('append')) ? this.utils.extendData(options, options['append']) : options;
  2237. }
  2238. });
  2239. $K.add('service', 'response', {
  2240. init: function(app)
  2241. {
  2242. this.app = app;
  2243. },
  2244. // public
  2245. parse: function(str)
  2246. {
  2247. if (str === '') return false;
  2248. var obj = (typeof str === 'object') ? str : JSON.parse(str);
  2249. if (obj[0] !== undefined)
  2250. {
  2251. for (var item in obj)
  2252. {
  2253. this._parseItem(obj[item]);
  2254. }
  2255. }
  2256. else
  2257. {
  2258. this._parseItem(obj);
  2259. }
  2260. return obj;
  2261. },
  2262. // private
  2263. _parseItem: function(item)
  2264. {
  2265. if (item.type === 'location')
  2266. {
  2267. top.location.href = item.data;
  2268. }
  2269. else if (item.type === 'message')
  2270. {
  2271. this.message.show(item.data);
  2272. }
  2273. else
  2274. {
  2275. for (var key in item.data)
  2276. {
  2277. var val = item.data[key];
  2278. var $el = $K.dom(key);
  2279. if (item.type === 'value')
  2280. {
  2281. val = (val === null || val === false) ? 0 : val;
  2282. val = (val === true) ? 1 : val;
  2283. $el.val(val);
  2284. }
  2285. else if (item.type === 'html')
  2286. {
  2287. val = (val === null || val === false) ? '' : val;
  2288. $el.html(this._stripslashes(val));
  2289. }
  2290. else if (item.type === 'addClass')
  2291. {
  2292. $el.addClass(val);
  2293. }
  2294. else if (item.type === 'removeClass')
  2295. {
  2296. $el.removeClass(val);
  2297. }
  2298. else if (item.type === 'show')
  2299. {
  2300. $el.removeClass('is-hidden');
  2301. }
  2302. else if (item.type === 'hide')
  2303. {
  2304. $el.addClass('is-hidden');
  2305. }
  2306. else if (item.type === 'animate')
  2307. {
  2308. this.animate.run($el, val);
  2309. }
  2310. }
  2311. }
  2312. return item;
  2313. },
  2314. _stripslashes: function(str)
  2315. {
  2316. return (str+'').replace(/\0/g, '0').replace(/\\([\\'"])/g, '$1');
  2317. }
  2318. });
  2319. $K.add('service', 'progress', {
  2320. init: function(app)
  2321. {
  2322. this.app = app;
  2323. this.$body = app.$body;
  2324. // defaults
  2325. this.defaults = {
  2326. selector: 'kube-progress',
  2327. target: false,
  2328. value: 100
  2329. }
  2330. // local
  2331. this.$progress = false;
  2332. this.$progressBar = false;
  2333. },
  2334. // public
  2335. stop: function()
  2336. {
  2337. this.$progress = false;
  2338. this.$progressBar = false;
  2339. $K.dom('#' + this.params.selector).remove();
  2340. if (this.params.target)
  2341. {
  2342. var $target = $K.dom(this.params.target);
  2343. $target.removeClass('is-relative');
  2344. }
  2345. },
  2346. show: function(params)
  2347. {
  2348. this._buildDefaults(params);
  2349. this._build();
  2350. },
  2351. hide: function(params)
  2352. {
  2353. if (this.$progress)
  2354. {
  2355. this._buildDefaults(params);
  2356. this.animate.run(this.$progress, 'fadeOut', this.stop.bind(this));
  2357. }
  2358. },
  2359. update: function(params)
  2360. {
  2361. this._buildDefaults(params);
  2362. if (!this.$progress) this._build();
  2363. this._setValue();
  2364. },
  2365. // private
  2366. _buildDefaults: function(data)
  2367. {
  2368. this.params = $K.extend({}, this.defaults, data);
  2369. },
  2370. _build: function()
  2371. {
  2372. this.stop();
  2373. this.$progress = $K.dom('<div>');
  2374. this.$progress.attr('id', this.params.selector);
  2375. this.$progress.addClass(this.params.selector);
  2376. this.$progressBar = $K.dom('<span>');
  2377. this.$progress.append(this.$progressBar);
  2378. if (this.params.target)
  2379. {
  2380. var $target = $K.dom(this.params.target);
  2381. if ($target.css('position') === 'static')
  2382. {
  2383. $target.addClass('is-relative');
  2384. }
  2385. $target.append(this.$progress);
  2386. }
  2387. else
  2388. {
  2389. this.$progress.addClass('is-fixed');
  2390. this.$body.append(this.$progress);
  2391. }
  2392. },
  2393. _setValue: function()
  2394. {
  2395. this.$progressBar.css('width', this.params.value + '%');
  2396. }
  2397. });
  2398. $K.add('service', 'message', {
  2399. init: function(app)
  2400. {
  2401. this.app = app;
  2402. // defaults
  2403. this.defaults = {
  2404. name: false,
  2405. delay: 7, // seconds
  2406. message: '',
  2407. position: 'right', // left, centered, line
  2408. positions: ['is-left', 'is-right', 'is-center', 'is-centered', 'is-line'],
  2409. type: false,
  2410. types: ['is-error', 'is-success', 'is-focus', 'is-black'],
  2411. selector: 'kube-message'
  2412. };
  2413. // animation
  2414. this.currentAnimation = [];
  2415. this.animation = {
  2416. line: ['slideInDown', 'slideOutUp'],
  2417. centered: ['slideInDown', 'slideOutUp'],
  2418. left: ['slideInLeft', 'slideOutLeft'],
  2419. right: ['slideInRight', 'slideOutRight']
  2420. };
  2421. // local
  2422. this.$message = false;
  2423. this.timeout = false;
  2424. },
  2425. // public
  2426. stop: function()
  2427. {
  2428. clearTimeout(this.timeout);
  2429. $K.dom('#' + this.params.selector).remove();
  2430. this.$message = false;
  2431. this.$doc.off('.kube.message');
  2432. },
  2433. show: function(params)
  2434. {
  2435. this._buildDefaults(params);
  2436. // stop
  2437. this.stop();
  2438. // build
  2439. this._build();
  2440. this._open();
  2441. },
  2442. hide: function(params)
  2443. {
  2444. this._buildDefaults(params);
  2445. this._close();
  2446. },
  2447. // private
  2448. _broadcast: function(message)
  2449. {
  2450. message = 'message.' + message;
  2451. message = (this.params.name !== false ) ? [this.params.name, message] : message;
  2452. this.app.broadcast(message, this);
  2453. },
  2454. _buildDefaults: function(data)
  2455. {
  2456. this.params = $K.extend({}, this.defaults, data);
  2457. },
  2458. _buildAnimation: function()
  2459. {
  2460. this.currentAnimation = this.animation[this.params.position];
  2461. },
  2462. _buildClose: function()
  2463. {
  2464. this.$message.on('click.kube.message', this._close.bind(this));
  2465. },
  2466. _buildType: function()
  2467. {
  2468. if (this.params.type)
  2469. {
  2470. this.$message.removeClass(this.params.types.join(' '));
  2471. this.$message.addClass(this.params.type);
  2472. }
  2473. },
  2474. _buildPosition: function()
  2475. {
  2476. this.$message.removeClass(this.params.positions.join(' '));
  2477. this.$message.addClass('is-' + this.params.position);
  2478. },
  2479. _buildMessage: function()
  2480. {
  2481. this.$message.html(this.params.message);
  2482. },
  2483. _build: function()
  2484. {
  2485. this.$message = $K.dom('<div>');
  2486. this.$message.attr('id', this.params.selector);
  2487. this.$message.addClass('message is-hidden');
  2488. this.$body.append(this.$message);
  2489. },
  2490. _handleKeyboard: function(e)
  2491. {
  2492. if (e.which === 27) this._close();
  2493. },
  2494. _open: function()
  2495. {
  2496. this._broadcast('open');
  2497. this._buildClose();
  2498. this._buildType();
  2499. this._buildPosition();
  2500. this._buildAnimation();
  2501. this._buildMessage();
  2502. this.animate.run(this.$message, this.currentAnimation[0], this._opened.bind(this));
  2503. },
  2504. _close: function(e)
  2505. {
  2506. if (this.$message)
  2507. {
  2508. this._broadcast('close');
  2509. this.animate.run(this.$message, this.currentAnimation[1], this._closed.bind(this));
  2510. }
  2511. },
  2512. _opened: function()
  2513. {
  2514. this.$doc.on('keyup.kube.message', this._handleKeyboard.bind(this));
  2515. this.timeout = setTimeout(this._close.bind(this), this.params.delay * 1000);
  2516. this._broadcast('opened');
  2517. },
  2518. _closed: function()
  2519. {
  2520. this.stop();
  2521. this._broadcast('closed');
  2522. }
  2523. });
  2524. $K.add('service', 'modal', {
  2525. init: function(app)
  2526. {
  2527. this.app = app;
  2528. // defaults
  2529. this.defaults = {
  2530. target: false,
  2531. name: false,
  2532. url: false,
  2533. title: false,
  2534. width: '600px',
  2535. height: false,
  2536. handle: false,
  2537. commands: false
  2538. };
  2539. // local
  2540. this.$box = false;
  2541. this.$modal = false;
  2542. },
  2543. // public
  2544. stop: function()
  2545. {
  2546. if (this.$box)
  2547. {
  2548. this.$box.remove();
  2549. this.$box = false;
  2550. this.$modal = false;
  2551. this.$doc.off('.kube.modal');
  2552. this.$win.off('.kube.modal');
  2553. }
  2554. if (this.$overlay)
  2555. {
  2556. this.$overlay.remove();
  2557. }
  2558. },
  2559. open: function(params)
  2560. {
  2561. this._buildDefaults(params);
  2562. if (this.params.url)
  2563. {
  2564. this._openUrl();
  2565. }
  2566. else if (this.params.target)
  2567. {
  2568. this._openTarget();
  2569. }
  2570. },
  2571. close: function()
  2572. {
  2573. this._close();
  2574. },
  2575. resize: function()
  2576. {
  2577. this.$modal.setWidth(this.params.width);
  2578. this.$modal.updatePosition();
  2579. },
  2580. // private
  2581. _broadcast: function(message)
  2582. {
  2583. message = 'modal.' + message;
  2584. this.app.broadcast([this.params.name, message], this, this.$modal, this.$modalForm);
  2585. },
  2586. _isOpened: function()
  2587. {
  2588. return (this.$modal && this.$modal.hasClass('is-open'));
  2589. },
  2590. _openUrl: function()
  2591. {
  2592. $K.ajax.post({
  2593. url: this.params.url,
  2594. success: this._doOpen.bind(this)
  2595. });
  2596. },
  2597. _openTarget: function()
  2598. {
  2599. var template = $K.dom(this.params.target).clone().html();
  2600. this._doOpen(template);
  2601. },
  2602. _doOpen: function(template)
  2603. {
  2604. this.stop();
  2605. if (!this._isDesktop())
  2606. {
  2607. document.activeElement.blur();
  2608. }
  2609. this._createModal(template);
  2610. this._buildModalBox();
  2611. this._buildOverlay();
  2612. this._buildModal();
  2613. this._buildModalForm();
  2614. this._buildModalCommands();
  2615. this.$modal.updatePosition();
  2616. this._broadcast('open');
  2617. this.animate.run(this.$box, 'fadeIn', this._opened.bind(this));
  2618. this.animate.run(this.$overlay, 'fadeIn');
  2619. },
  2620. _opened: function()
  2621. {
  2622. this.$modal.addClass('is-open');
  2623. this.$box.on('mousedown.kube.modal', this._close.bind(this));
  2624. this.$doc.on('keyup.kube.modal', this._handleEscape.bind(this));
  2625. this.$win.on('resize.kube.modal', this.resize.bind(this));
  2626. this.$modal.getBody().find('input[type=text],input[type=url],input[type=email]').on('keydown.kube.modal', this._handleEnter.bind(this));
  2627. this._broadcast('opened');
  2628. },
  2629. _close: function(e)
  2630. {
  2631. if (!this.$box || !this._isOpened()) return;
  2632. if (e)
  2633. {
  2634. if (!this._needToClose(e.target))
  2635. {
  2636. return;
  2637. }
  2638. e.stopPropagation();
  2639. e.preventDefault();
  2640. }
  2641. this._broadcast('close');
  2642. this.animate.run(this.$box, 'fadeOut', this._closed.bind(this));
  2643. this.animate.run(this.$overlay, 'fadeOut');
  2644. },
  2645. _closed: function()
  2646. {
  2647. this.$modal.removeClass('is-open');
  2648. this.$box.off('.kube.modal');
  2649. this.$doc.off('.kube.modal');
  2650. this.$win.off('.kube.modal');
  2651. this._broadcast('closed');
  2652. },
  2653. _createModal: function(template)
  2654. {
  2655. this.$modal = $K.create('class.modal.element', this.app, template);
  2656. },
  2657. _buildDefaults: function(data)
  2658. {
  2659. this.params = $K.extend({}, this.defaults, data);
  2660. },
  2661. _buildModalBox: function()
  2662. {
  2663. this.$box = $K.dom('<div>');
  2664. this.$box.attr('id', 'kube-modal');
  2665. this.$box.addClass('modal-box is-hidden');
  2666. this.$box.html('');
  2667. this.$body.append(this.$box);
  2668. },
  2669. _buildOverlay: function()
  2670. {
  2671. this.$overlay = $K.dom('#kube-overlay');
  2672. if (this.$overlay.length === 0)
  2673. {
  2674. this.$overlay = $K.dom('<div>');
  2675. this.$overlay.attr('id', 'kube-overlay');
  2676. this.$overlay.addClass('overlay is-hidden');
  2677. this.$body.prepend(this.$overlay);
  2678. }
  2679. },
  2680. _buildModal: function()
  2681. {
  2682. this.$box.append(this.$modal);
  2683. this.$modal.setTitle(this.params.title);
  2684. this.$modal.setHeight(this.params.height);
  2685. this.$modal.setWidth(this.params.width);
  2686. },
  2687. _buildModalCommands: function()
  2688. {
  2689. if (this.params.commands)
  2690. {
  2691. var commands = this.params.commands;
  2692. var $footer = this.$modal.getFooter();
  2693. for (var key in commands)
  2694. {
  2695. var $btn = $K.dom('<button>');
  2696. $btn.addClass('button');
  2697. $btn.html(commands[key].title);
  2698. $btn.attr('data-command', key);
  2699. if (typeof commands[key].classname !== 'undefined')
  2700. {
  2701. $btn.addClass(commands[key].classname);
  2702. }
  2703. if (typeof commands[key].close !== 'undefined')
  2704. {
  2705. $btn.attr('data-action', 'close');
  2706. $btn.on('click', this._close.bind(this));
  2707. }
  2708. else
  2709. {
  2710. $btn.on('click', this._handleCommand.bind(this));
  2711. }
  2712. $footer.append($btn);
  2713. }
  2714. }
  2715. },
  2716. _buildModalForm: function()
  2717. {
  2718. this.$modalForm = $K.create('modal.form', this.app, this.$modal.getForm());
  2719. },
  2720. _needToClose: function(el)
  2721. {
  2722. var $target = $K.dom(el);
  2723. if ($target.attr('data-action') === 'close' || this.$modal.isCloseNode(el) || $target.closest('.modal').length === 0)
  2724. {
  2725. return true;
  2726. }
  2727. return false;
  2728. },
  2729. _handleCommand: function(e)
  2730. {
  2731. var $btn = $K.dom(e.target).closest('button');
  2732. var command = $btn.attr('data-command');
  2733. if (command !== 'cancel') e.preventDefault();
  2734. this._broadcast(command);
  2735. },
  2736. _handleEnter: function(e)
  2737. {
  2738. if (e.which === 13)
  2739. {
  2740. if (this.params.handle)
  2741. {
  2742. e.preventDefault();
  2743. this._broadcast(this.params.handle);
  2744. }
  2745. }
  2746. },
  2747. _handleEscape: function(e)
  2748. {
  2749. if (e.which === 27) this._close();
  2750. },
  2751. _isDesktop: function()
  2752. {
  2753. return !/(iPhone|iPod|iPad|Android)/.test(navigator.userAgent);
  2754. }
  2755. });
  2756. $K.add('class', 'modal.form', {
  2757. extends: ['dom'],
  2758. init: function(app, element)
  2759. {
  2760. this.app = app;
  2761. // build
  2762. this.build(element);
  2763. },
  2764. // public
  2765. build: function(element)
  2766. {
  2767. this.parse(element);
  2768. },
  2769. getData: function()
  2770. {
  2771. var data = {};
  2772. this.find('[name]').each(function(node)
  2773. {
  2774. var $node = $K.dom(node);
  2775. data[$node.attr('name')] = $node.val();
  2776. });
  2777. return data;
  2778. },
  2779. setData: function(data)
  2780. {
  2781. this.find('[name]').each(function(node)
  2782. {
  2783. var $node = $K.dom(node);
  2784. var name = $node.attr('name');
  2785. if (data.hasOwnProperty(name))
  2786. {
  2787. if (node.type && node.type === 'checkbox') node.checked = data[name];
  2788. else $node.val(data[name]);
  2789. }
  2790. });
  2791. },
  2792. getItem: function(name)
  2793. {
  2794. return this.find('[name=' + name + ']');
  2795. }
  2796. });
  2797. $K.add('class', 'modal.element', {
  2798. extends: ['dom'],
  2799. init: function(app, template)
  2800. {
  2801. this.app = app;
  2802. this.opts = app.opts;
  2803. this.$win = app.$win;
  2804. // init
  2805. this._init(template);
  2806. },
  2807. // get
  2808. getForm: function()
  2809. {
  2810. return this.find('form');
  2811. },
  2812. getHeader: function()
  2813. {
  2814. return this.$modalHeader;
  2815. },
  2816. getBody: function()
  2817. {
  2818. return this.$modalBody;
  2819. },
  2820. getFooter: function()
  2821. {
  2822. return this.$modalFooter;
  2823. },
  2824. // set
  2825. setTitle: function(title)
  2826. {
  2827. if (title) this.$modalHeader.html(title);
  2828. },
  2829. setWidth: function(width)
  2830. {
  2831. width = (parseInt(width) >= this.$win.width()) ? '96%' : width;
  2832. this.css('max-width', width);
  2833. },
  2834. setHeight: function(height)
  2835. {
  2836. if (height !== false) this.$modalBody.css('height', height);
  2837. },
  2838. // update
  2839. updatePosition: function()
  2840. {
  2841. var width = this.width();
  2842. this.css({ 'left': '50%', 'margin-left': '-' + (width/2) + 'px' });
  2843. var windowHeight = this.$win.height();
  2844. var height = this.height();
  2845. var marginTop = (windowHeight/2 - height/2);
  2846. if (height < windowHeight && marginTop !== 0)
  2847. {
  2848. this.css('margin-top', marginTop + 'px');
  2849. }
  2850. },
  2851. // is
  2852. isCloseNode: function(el)
  2853. {
  2854. return (el === this.$modalClose.get());
  2855. },
  2856. // private
  2857. _init: function(template)
  2858. {
  2859. this._build();
  2860. this._buildClose();
  2861. this._buildHeader();
  2862. this._buildBody();
  2863. this._buildFooter();
  2864. this._buildTemplate(template);
  2865. },
  2866. _build: function()
  2867. {
  2868. this.parse('<div>');
  2869. this.addClass('modal');
  2870. this.attr('dir', this.opts.direction);
  2871. },
  2872. _buildClose: function()
  2873. {
  2874. this.$modalClose = $K.dom('<span>');
  2875. this.$modalClose.addClass('close');
  2876. this.append(this.$modalClose);
  2877. },
  2878. _buildHeader: function()
  2879. {
  2880. this.$modalHeader = $K.dom('<div>');
  2881. this.$modalHeader.addClass('modal-header');
  2882. this.append(this.$modalHeader);
  2883. },
  2884. _buildBody: function()
  2885. {
  2886. this.$modalBody = $K.dom('<div>');
  2887. this.$modalBody.addClass('modal-body');
  2888. this.append(this.$modalBody);
  2889. },
  2890. _buildFooter: function()
  2891. {
  2892. this.$modalFooter = $K.dom('<div>');
  2893. this.$modalFooter.addClass('modal-footer');
  2894. this.append(this.$modalFooter);
  2895. },
  2896. _buildTemplate: function(template)
  2897. {
  2898. this.$modalBody.html(template);
  2899. }
  2900. });
  2901. $K.add('service', 'observer', {
  2902. init: function(app)
  2903. {
  2904. this.app = app;
  2905. this.opts = app.opts;
  2906. if (this._isObserve())
  2907. {
  2908. this._build();
  2909. }
  2910. },
  2911. // private
  2912. _isObserve: function()
  2913. {
  2914. return (typeof this.opts.observer !== 'undefined' && window.MutationObserver);
  2915. },
  2916. _build: function()
  2917. {
  2918. var self = this;
  2919. var observer = new MutationObserver(function(mutations)
  2920. {
  2921. mutations.forEach(function(mutation)
  2922. {
  2923. var newNodes = mutation.addedNodes;
  2924. if (newNodes.length === 0 || (newNodes.length === 1 && newNodes.nodeType === 3))
  2925. {
  2926. return;
  2927. }
  2928. self._iterate();
  2929. });
  2930. });
  2931. // pass in the target node, as well as the observer options
  2932. observer.observe(document, {
  2933. subtree: true,
  2934. childList: true
  2935. });
  2936. },
  2937. _iterate: function()
  2938. {
  2939. var self = this;
  2940. var $nodes = $K.dom('[data-kube]').not('[data-loaded]');
  2941. $nodes.each(function(node, i)
  2942. {
  2943. var $el = $K.dom(node);
  2944. var name = $el.attr('data-kube');
  2945. var id = ($el.attr('id')) ? $el.attr('id') : name + '-' + (self.app.servicesIndex + i);
  2946. var instance = new App.Module(self.app, $el, name, id);
  2947. self._storeElementModule(instance, name, id)
  2948. self._call(instance, 'start');
  2949. });
  2950. // $R
  2951. if (typeof $R !== 'undefined')
  2952. {
  2953. $R('[data-redactor]');
  2954. }
  2955. },
  2956. _call: function(instance, method, args)
  2957. {
  2958. if (typeof instance[method] === 'function')
  2959. {
  2960. return instance[method].apply(instance, args);
  2961. }
  2962. },
  2963. _storeElementModule: function(instance, name, id)
  2964. {
  2965. if (instance)
  2966. {
  2967. if (typeof this.app.modules[name] === 'undefined')
  2968. {
  2969. this.app.modules[name] = {};
  2970. }
  2971. this.app.modules[name][id] = instance;
  2972. }
  2973. }
  2974. });
  2975. $K.add('service', 'utils', {
  2976. init: function(app)
  2977. {
  2978. this.app = app;
  2979. },
  2980. // string
  2981. parseOptsString: function(str)
  2982. {
  2983. var properties = str.replace('{', '').replace('}', '').trim().replace(/;$/, '').split(';');
  2984. var obj = {};
  2985. properties.forEach(function(property) {
  2986. var tup = property.split(':');
  2987. obj[tup[0].trim()] = tup[1].trim().replace(/'/g, '');
  2988. });
  2989. return obj;
  2990. },
  2991. ucfirst: function(str)
  2992. {
  2993. return str.charAt(0).toUpperCase() + str.slice(1);
  2994. },
  2995. // object
  2996. checkProperty: function(obj)
  2997. {
  2998. var args = (arguments[1] && Array.isArray(arguments[1])) ? arguments[1] : [].slice.call(arguments, 1);
  2999. for (var i = 0; i < args.length; i++)
  3000. {
  3001. if (!obj || (typeof obj[args[i]] === 'undefined'))
  3002. {
  3003. return false;
  3004. }
  3005. obj = obj[args[i]];
  3006. }
  3007. return obj;
  3008. },
  3009. // data
  3010. extendData: function(data, elements)
  3011. {
  3012. if (typeof elements === 'object')
  3013. {
  3014. data = $K.extend({}, data, elements);
  3015. }
  3016. else if (typeof elements === 'string')
  3017. {
  3018. var $elms = $K.dom(elements);
  3019. $elms.each(function(node)
  3020. {
  3021. var $node = $K.dom(node);
  3022. if (node.tagName === 'FORM')
  3023. {
  3024. data = $K.extend({}, data, $node.serialize(true));
  3025. }
  3026. else
  3027. {
  3028. var name = ($node.attr('name')) ? $node.attr('name') : $node.attr('id');
  3029. var val = $node.val();
  3030. data[name] = (this._isNumber(val)) ? parseFloat(val) : this._getBooleanFromStr(val);
  3031. }
  3032. });
  3033. }
  3034. return data;
  3035. },
  3036. _isNumber: function(str)
  3037. {
  3038. return !isNaN(str) && !isNaN(parseFloat(str));
  3039. },
  3040. _getBooleanFromStr: function(str)
  3041. {
  3042. if (str === 'true') return true;
  3043. else if (str === 'false') return false;
  3044. return str;
  3045. }
  3046. });
  3047. window.Kube = window.$K = $K;
  3048. }());
  3049. (function($K)
  3050. {
  3051. $K.add('module', 'alert', {
  3052. init: function(app, context)
  3053. {
  3054. this.app = app;
  3055. this.animate = app.animate;
  3056. // context
  3057. this.context = context;
  3058. this.$element = context.getElement();
  3059. },
  3060. // events
  3061. onclick: function(e, element, type)
  3062. {
  3063. if (type === 'close')
  3064. {
  3065. this.close(e);
  3066. }
  3067. },
  3068. // public
  3069. open: function(e)
  3070. {
  3071. if (this.$element.isOpened()) return;
  3072. if (e) e.preventDefault();
  3073. this.app.broadcast('alert.open', this);
  3074. this.animate.run(this.$element, 'fadeIn', this._opened.bind(this));
  3075. },
  3076. close: function(e)
  3077. {
  3078. if (this.$element.isClosed()) return;
  3079. if (e) e.preventDefault();
  3080. this.app.broadcast('alert.close', this);
  3081. this.animate.run(this.$element, 'fadeOut', this._closed.bind(this));
  3082. },
  3083. // private
  3084. _opened: function()
  3085. {
  3086. this.app.broadcast('alert.opened', this);
  3087. },
  3088. _closed: function()
  3089. {
  3090. this.app.broadcast('alert.closed', this);
  3091. }
  3092. });
  3093. })(Kube);
  3094. (function($K)
  3095. {
  3096. $K.add('module', 'toggle', {
  3097. init: function(app, context)
  3098. {
  3099. this.app = app;
  3100. this.animate = app.animate;
  3101. // defaults
  3102. var defaults = {
  3103. target: false
  3104. };
  3105. // context
  3106. this.context = context;
  3107. this.params = context.getParams(defaults);
  3108. this.$element = context.getElement();
  3109. this.$target = context.getTarget();
  3110. },
  3111. // public
  3112. start: function()
  3113. {
  3114. this.$element.on('click.kube.toggle', this.toggle.bind(this));
  3115. },
  3116. stop: function()
  3117. {
  3118. this.$element.off('.kube.toggle');
  3119. },
  3120. toggle: function(e)
  3121. {
  3122. return (this.$target.isOpened()) ? this.close(e) : this.open(e);
  3123. },
  3124. open: function(e)
  3125. {
  3126. if (this.$target.isOpened()) return;
  3127. if (e) e.preventDefault();
  3128. this.app.broadcast('toggle.open', this);
  3129. this.animate.run(this.$target, 'slideDown', this._opened.bind(this));
  3130. },
  3131. close: function(e)
  3132. {
  3133. if (this.$target.isClosed()) return;
  3134. if (e) e.preventDefault();
  3135. this.app.broadcast('toggle.close', this);
  3136. this.animate.run(this.$target, 'slideUp', this._closed.bind(this));
  3137. },
  3138. // private
  3139. _opened: function()
  3140. {
  3141. this.app.broadcast('toggle.opened', this);
  3142. },
  3143. _closed: function()
  3144. {
  3145. this.app.broadcast('toggle.closed', this);
  3146. }
  3147. });
  3148. })(Kube);
  3149. (function($K)
  3150. {
  3151. $K.add('module', 'sticky', {
  3152. init: function(app, context)
  3153. {
  3154. this.app = app;
  3155. this.$win = app.$win;
  3156. // defaults
  3157. var defaults = {
  3158. offset: 0 // string in pixels
  3159. };
  3160. // context
  3161. this.context = context;
  3162. this.params = context.getParams(defaults);
  3163. this.$element = context.getElement();
  3164. },
  3165. start: function()
  3166. {
  3167. this.offsetTop = this._getOffsetTop();
  3168. this._load();
  3169. this.$win.on('scroll.kube.sticky', this._load.bind(this));
  3170. },
  3171. stop: function()
  3172. {
  3173. this.$win.off('scroll.kube.sticky');
  3174. this.$element.removeClass('fixed').css('top', '');
  3175. },
  3176. // private
  3177. _load: function()
  3178. {
  3179. return (this._isFix()) ? this._setFixed() : this._setUnfixed();
  3180. },
  3181. _isFix: function()
  3182. {
  3183. return (this.$win.scrollTop() > (this.offsetTop + parseInt(this.params.offset, 10)));
  3184. },
  3185. _setFixed: function()
  3186. {
  3187. this.$element.addClass('is-fixed').css('top', this.params.offset);
  3188. this.app.broadcast('sticky.fixed', this);
  3189. },
  3190. _setUnfixed: function()
  3191. {
  3192. this.$element.removeClass('is-fixed').css('top', '');
  3193. this.app.broadcast('sticky.unfixed', this);
  3194. },
  3195. _getOffsetTop: function()
  3196. {
  3197. return this.$element.offset().top;
  3198. }
  3199. });
  3200. })(Kube);
  3201. (function($K)
  3202. {
  3203. $K.add('module', 'offcanvas', {
  3204. init: function(app, context)
  3205. {
  3206. this.app = app;
  3207. this.$doc = app.$doc;
  3208. this.$body = app.$body;
  3209. this.utils = app.utils;
  3210. this.animate = app.animate;
  3211. this.transition = app.transition;
  3212. // defaults
  3213. var defaults = {
  3214. clickOutside: true,
  3215. target: false
  3216. };
  3217. // context
  3218. this.context = context;
  3219. this.params = context.getParams(defaults);
  3220. this.$element = context.getElement();
  3221. this.$target = context.getTarget();
  3222. // build
  3223. this._build();
  3224. },
  3225. start: function()
  3226. {
  3227. this.$element.on('click.kube.offcanvas', this.toggle.bind(this));
  3228. },
  3229. stop: function()
  3230. {
  3231. this._clear();
  3232. },
  3233. toggle: function(e)
  3234. {
  3235. return (this.$target.isOpened()) ? this.close(e) : this.open(e);
  3236. },
  3237. open: function(e)
  3238. {
  3239. if (e)
  3240. {
  3241. e.stopPropagation();
  3242. e.preventDefault();
  3243. }
  3244. this._clear();
  3245. this.$body.addClass('is-no-scroll-x');
  3246. this.$target.addClass('is-offcanvas');
  3247. this.targetWidth = this.$target.width();
  3248. this._resize();
  3249. this.app.broadcast('offcanvas.open', this);
  3250. return (this.isSlide) ? this._openSlide() : this._openPush();
  3251. },
  3252. close: function(e)
  3253. {
  3254. if (this.eventScroll) return;
  3255. if (e)
  3256. {
  3257. var $el = $K.dom(e.target);
  3258. var el = $el.get();
  3259. var isClickable = (el.tagName === 'A' ||el.tagName === 'BUTTON');
  3260. if (!isClickable || el === this.$element.get())
  3261. {
  3262. e.stopPropagation();
  3263. e.preventDefault();
  3264. }
  3265. }
  3266. this.app.broadcast('offcanvas.close', this);
  3267. return (this.isSlide) ? this._closeSlide() : this._closePush();
  3268. },
  3269. // private
  3270. _build: function()
  3271. {
  3272. this.isSlide = !(this.$target.hasClass('is-offcanvas-push'));
  3273. this.slideDirection = (this.$target.hasClass('is-offcanvas-right')) ? 'Right' : 'Left';
  3274. this.pushSign = (this.slideDirection === 'Left') ? '' : '-';
  3275. this.eventScroll = false;
  3276. },
  3277. _handleKeyboard: function(e)
  3278. {
  3279. if (e.which === 27) this.close();
  3280. },
  3281. _openSlide: function()
  3282. {
  3283. this.animate.run(this.$target, 'slideIn' + this.slideDirection, this._opened.bind(this));
  3284. },
  3285. _openPush: function()
  3286. {
  3287. this.$target.show();
  3288. this._pushBody(this.pushSign + this.targetWidth + 'px', this._opened.bind(this));
  3289. },
  3290. _opened: function()
  3291. {
  3292. this.$doc.on('touchmove.kube.offcanvas', function() { this.eventScroll = true; }.bind(this));
  3293. this.$doc.on('touchstart.kube.offcanvas', function() { this.eventScroll = false; }.bind(this));
  3294. this.$doc.on('keyup.kube.offcanvas', this._handleKeyboard.bind(this));
  3295. if (this.params.clickOutside)
  3296. {
  3297. this.$doc.on('click.kube.offcanvas touchend.kube.offcanvas', this.close.bind(this));
  3298. }
  3299. this.app.broadcast('offcanvas.opened', this);
  3300. },
  3301. _closeSlide: function()
  3302. {
  3303. this.animate.run(this.$target, 'slideOut' + this.slideDirection, this._closed.bind(this));
  3304. },
  3305. _closePush: function()
  3306. {
  3307. this._pushBody('0', this._closed.bind(this));
  3308. },
  3309. _closed: function()
  3310. {
  3311. this.$doc.off('.kube.offcanvas');
  3312. this.$body.removeClass('is-no-scroll-x');
  3313. this.transition.remove(this.$body);
  3314. this.$target.removeClass('is-offcanvas');
  3315. this.$target.hide();
  3316. this.app.broadcast('offcanvas.closed', this);
  3317. },
  3318. _pushBody: function(transform, callback)
  3319. {
  3320. var params = {
  3321. classname: 'is-offcanvasTransition',
  3322. css: { transform: 'translateX(' + transform + ')' },
  3323. callback: callback
  3324. };
  3325. this.transition.run(this.$body, params, callback);
  3326. },
  3327. _resize: function()
  3328. {
  3329. var resize = function()
  3330. {
  3331. this.$target.height(this.$doc.height());
  3332. }.bind(this);
  3333. resize();
  3334. this.$doc.on('resize.kube.offcanvas', resize);
  3335. },
  3336. _clear: function()
  3337. {
  3338. this.$doc.off('.kube.offcanvas');
  3339. this.transition.remove(this.$body);
  3340. $K.dom('.is-offcanvas').each(function(node)
  3341. {
  3342. var $el = $K.dom(node);
  3343. this.animate.remove($el);
  3344. $el.hide();
  3345. $el.removeClass('is-offcanvas');
  3346. }.bind(this));
  3347. }
  3348. });
  3349. })(Kube);
  3350. (function($K)
  3351. {
  3352. $K.add('module', 'tabs', {
  3353. init: function(app, context)
  3354. {
  3355. this.app = app;
  3356. this.$body = app.$body;
  3357. // defaults
  3358. var defaults = {
  3359. equal: false
  3360. };
  3361. // context
  3362. this.context = context;
  3363. this.params = context.getParams(defaults);
  3364. this.$element = context.getElement();
  3365. // local
  3366. this.$boxes = $K.dom([]);
  3367. this.$tabActive = false;
  3368. this.$boxActive = false;
  3369. },
  3370. start: function()
  3371. {
  3372. this._buildControls();
  3373. this._buildBoxes();
  3374. this._setEqualBoxes();
  3375. this._open();
  3376. },
  3377. stop: function()
  3378. {
  3379. this.$tabsControls.off('.kube.tabs');
  3380. },
  3381. // api
  3382. getActiveTab: function()
  3383. {
  3384. return this.$tabActive;
  3385. },
  3386. getActiveBox: function()
  3387. {
  3388. return this.$boxActive;
  3389. },
  3390. // private
  3391. _toggle: function(e)
  3392. {
  3393. if (e)
  3394. {
  3395. e.stopPropagation();
  3396. e.preventDefault();
  3397. }
  3398. var $tab = $K.dom(e.target);
  3399. var $box = this._getBox($tab);
  3400. if ($tab.hasClass('is-active')) return;
  3401. this._open($tab);
  3402. this.app.broadcast('tabs.opened', this);
  3403. },
  3404. _buildControls: function()
  3405. {
  3406. this.$tabsControls = this.$element.find('a');
  3407. this.$tabsControls.on('click.kube.tabs', this._toggle.bind(this));
  3408. },
  3409. _buildBoxes: function()
  3410. {
  3411. this.$tabsControls.each(function(node, i)
  3412. {
  3413. var $tab = $K.dom(node);
  3414. var $box = this._getBox($tab);
  3415. this.$boxes.add($box);
  3416. if (i === 0) this.$tabActive = $tab;
  3417. if ($tab.hasClass('is-active')) this.$tabActive = $tab;
  3418. }.bind(this));
  3419. },
  3420. _open: function($tab)
  3421. {
  3422. this.$tabActive = ($tab) ? $tab : this.$tabActive;
  3423. this.$tabsControls.removeClass('is-active');
  3424. this.$tabActive.addClass('is-active');
  3425. this.$boxActive = this._getBox(this.$tabActive);
  3426. this.$boxes.addClass('is-hidden').removeClass('is-open');
  3427. this.$boxActive.removeClass('is-hidden').addClass('is-open');
  3428. },
  3429. _getBox: function($tab)
  3430. {
  3431. return $K.dom($tab.attr('href'));
  3432. },
  3433. _setEqualBoxes: function()
  3434. {
  3435. if (!this.params.equal) return;
  3436. var minHeight = this._getItemMaxHeight() + 'px';
  3437. this.$boxes.css('min-height', minHeight);
  3438. },
  3439. _getItemMaxHeight: function()
  3440. {
  3441. var max = 0;
  3442. this.$boxes.each(function(node)
  3443. {
  3444. var $node = $K.dom(node);
  3445. var h = $node.height();
  3446. max = (h > max) ? h : max;
  3447. });
  3448. return max;
  3449. }
  3450. });
  3451. })(Kube);
  3452. (function($K)
  3453. {
  3454. $K.add('module', 'dropdown', {
  3455. init: function(app, context)
  3456. {
  3457. this.app = app;
  3458. this.$doc = app.$doc;
  3459. this.$win = app.$win;
  3460. this.$body = app.$body;
  3461. this.utils = app.utils;
  3462. this.animate = app.animate;
  3463. // defaults
  3464. var defaults = {
  3465. target: false
  3466. };
  3467. // context
  3468. this.context = context;
  3469. this.params = context.getParams(defaults);
  3470. this.$element = context.getElement();
  3471. this.$target = context.getTarget();
  3472. // local
  3473. this.animationOpen = 'slideDown';
  3474. this.animationClose = 'slideUp';
  3475. },
  3476. // public
  3477. start: function()
  3478. {
  3479. this.$element.on('click.kube.dropdown', this.toggle.bind(this));
  3480. },
  3481. stop: function()
  3482. {
  3483. this.animate.clear(this.$target);
  3484. this.$target.hide();
  3485. this.$element.off('.kube.dropdown');
  3486. this.$doc.off('.kube.dropdown');
  3487. this.$win.off('.kube.dropdown');
  3488. },
  3489. toggle: function(e)
  3490. {
  3491. return (this.$target.isOpened()) ? this.close(e) : this.open(e);
  3492. },
  3493. open: function(e)
  3494. {
  3495. if (this.$target.isOpened()) return;
  3496. if (e)
  3497. {
  3498. e.stopPropagation();
  3499. e.preventDefault();
  3500. }
  3501. this.$doc.off('.kube.dropdown');
  3502. this.$win.off('.kube.dropdown');
  3503. // hide all
  3504. this.$body.find('.dropdown').each(function(node)
  3505. {
  3506. var $el = $K.dom(node);
  3507. this.animate.remove($el);
  3508. $el.hide();
  3509. }.bind(this));
  3510. this._openCaret();
  3511. this._setPosition();
  3512. this.$element.addClass('dropdown-in');
  3513. this.app.broadcast('dropdown.open', this);
  3514. this.animate.run(this.$target, this.animationOpen, this._opened.bind(this));
  3515. },
  3516. close: function(e)
  3517. {
  3518. if (this.$target.isClosed()) return;
  3519. if (e)
  3520. {
  3521. var el = e.target;
  3522. var $el = $K.dom(el);
  3523. var isClickable = (el.tagName === 'A' || el.tagName === 'BUTTON');
  3524. if (!isClickable || el === this.$element.get() || (el.tagName === 'A' && $el.hasClass('is-active')))
  3525. {
  3526. e.stopPropagation();
  3527. e.preventDefault();
  3528. }
  3529. }
  3530. this.app.broadcast('dropdown.close', this);
  3531. this.animate.run(this.$target, this.animationClose, this._closed.bind(this));
  3532. },
  3533. // private
  3534. _getPlacement: function()
  3535. {
  3536. var pos = this.$element.position();
  3537. var height = parseFloat(this.$element.css('height')) + pos.top + parseFloat(this.$target.css('height'));
  3538. return (this.$doc.height() < height) ? 'top' : 'bottom';
  3539. },
  3540. _setPosition: function()
  3541. {
  3542. var elHeight = parseFloat(this.$element.css('height'));
  3543. var pos = this.$element.offset();
  3544. var top = pos.top + elHeight;
  3545. var left = pos.left;
  3546. var height = parseFloat(this.$target.css('height'));
  3547. var placement = this._getPlacement();
  3548. var width = parseFloat(this.$target.css('width'));
  3549. var borderWidth = parseFloat(this.$element.css('border-left-width')) + parseFloat(this.$element.css('border-right-width'));
  3550. var leftFix = (this.$win.width() < (left + width)) ? (width - this.$element.width() - borderWidth) : 0;
  3551. if (placement === 'top')
  3552. {
  3553. top = top - height - elHeight;
  3554. this.animationOpen = 'show';
  3555. this.animationClose = 'hide';
  3556. }
  3557. else
  3558. {
  3559. this.animationOpen = 'slideDown';
  3560. this.animationClose = 'slideUp';
  3561. }
  3562. this.$target.css({ 'top': top + 'px', 'left': (left - leftFix) + 'px' });
  3563. },
  3564. _handleKeyboard: function(e)
  3565. {
  3566. if (e.which === 27) this.close();
  3567. },
  3568. _opened: function()
  3569. {
  3570. this.$doc.on('keyup.kube.dropdown', this._handleKeyboard.bind(this));
  3571. this.$doc.on('click.kube.dropdown touchstart.kube.dropdown', this.close.bind(this));
  3572. this.$doc.on('scroll.kube.dropdown', this._setPosition.bind(this));
  3573. this.$win.on('resize.kube.dropdown', this._setPosition.bind(this));
  3574. this.app.broadcast('dropdown.opened', this);
  3575. },
  3576. _closed: function()
  3577. {
  3578. this.$doc.off('.kube.dropdown');
  3579. this.$win.off('.kube.dropdown');
  3580. this._closeCaret();
  3581. this.$element.removeClass('dropdown-in');
  3582. this.app.broadcast('dropdown.closed', this);
  3583. },
  3584. _openCaret: function()
  3585. {
  3586. var $caret = this.$element.find('.caret');
  3587. $caret.removeClass('is-down').addClass('is-left');
  3588. },
  3589. _closeCaret: function()
  3590. {
  3591. var $caret = this.$element.find('.caret');
  3592. $caret.removeClass('is-left').addClass('is-down');
  3593. }
  3594. });
  3595. })(Kube);