QuoJS.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140
  1. /*!
  2. * QuoJS 1.0.1 ~ Copyright (c) 2011, 2012 Javi Jiménez Villar (@soyjavi)
  3. * http://quojs.tapquo.com
  4. * Released under MIT license, https://raw.github.com/soyjavi/QuoJS/master/LICENSE.txt
  5. */
  6. var Quo = (function() {
  7. var EMPTY_ARRAY = [];
  8. function Q(dom, selector) {
  9. dom = dom || EMPTY_ARRAY;
  10. dom.__proto__ = Q.prototype;
  11. dom.selector = selector || '';
  12. return dom;
  13. };
  14. /**
  15. * ?
  16. */
  17. function $$(selector) {
  18. if (!selector) {
  19. return Q();
  20. } else {
  21. var domain_selector = $$.getDomainSelector(selector);
  22. return Q(domain_selector, selector);
  23. }
  24. };
  25. /**
  26. * ?
  27. */
  28. $$.extend = function(target) {
  29. Array.prototype.slice.call(arguments, 1).forEach(function(source) {
  30. for (key in source) target[key] = source[key];
  31. })
  32. return target;
  33. };
  34. Q.prototype = $$.fn = {};
  35. return $$;
  36. })();
  37. window.Quo = Quo;
  38. '$$' in window || (window.$$ = Quo);
  39. (function($$) {
  40. var OBJ_PROTO = Object.prototype;
  41. var EMPTY_ARRAY = [];
  42. /**
  43. * Determine the internal JavaScript [[Class]] of an object.
  44. *
  45. * @param {object} obj to get the real type of itself.
  46. * @return {string} with the internal JavaScript [[Class]] of itself.
  47. */
  48. $$.toType = function(obj) {
  49. return OBJ_PROTO.toString.call(obj).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
  50. };
  51. /**
  52. * ?
  53. */
  54. $$.isOwnProperty = function(object, property) {
  55. return OBJ_PROTO.hasOwnProperty.call(object, property);
  56. };
  57. /**
  58. * ?
  59. */
  60. $$.getDomainSelector = function(selector) {
  61. var domain = null;
  62. var elementTypes = [1, 9, 11];
  63. var type = $$.toType(selector);
  64. if (type === 'array') {
  65. domain = _compact(selector);
  66. } else if (type === 'string') {
  67. domain = $$.query(document, selector);
  68. } else if (elementTypes.indexOf(selector.nodeType) >= 0 || selector === window) {
  69. domain = [selector];
  70. selector = null;
  71. }
  72. return domain;
  73. };
  74. /**
  75. * ?
  76. */
  77. $$.map = function(elements, callback) {
  78. //@TODO: Refactor!!!
  79. var values = [];
  80. var i;
  81. var key;
  82. if ($$.toType(elements) === 'array') {
  83. for (i = 0; i < elements.length; i++) {
  84. var value = callback(elements[i], i);
  85. if (value != null) values.push(value);
  86. }
  87. } else {
  88. for (key in elements) {
  89. value = callback(elements[key], key);
  90. if (value != null) values.push(value);
  91. }
  92. }
  93. return _flatten(values);
  94. };
  95. /**
  96. * ?
  97. */
  98. $$.each = function(elements, callback) {
  99. var i, key;
  100. if ($$.toType(elements) === 'array')
  101. for(i = 0; i < elements.length; i++) {
  102. if(callback.call(elements[i], i, elements[i]) === false) return elements;
  103. }
  104. else
  105. for(key in elements) {
  106. if(callback.call(elements[key], key, elements[key]) === false) return elements;
  107. }
  108. return elements;
  109. };
  110. /**
  111. * ?
  112. */
  113. $$.mix = function() {
  114. var child = {};
  115. for (var arg = 0, len = arguments.length; arg < len; arg++) {
  116. var argument = arguments[arg];
  117. for (var prop in argument) {
  118. if ($$.isOwnProperty(argument, prop) && argument[prop] !== undefined) {
  119. child[prop] = argument[prop];
  120. }
  121. }
  122. }
  123. return child;
  124. };
  125. /**
  126. * ?
  127. */
  128. $$.fn.forEach = EMPTY_ARRAY.forEach;
  129. /**
  130. * ?
  131. */
  132. $$.fn.indexOf = EMPTY_ARRAY.indexOf;
  133. /**
  134. * ?
  135. */
  136. $$.fn.map = function(fn){
  137. return $$.map(this, function(el, i){ return fn.call(el, i, el) });
  138. };
  139. /**
  140. * ?
  141. */
  142. /*
  143. $$.fn.slice = function(){
  144. return $$(slice.apply(this, arguments));
  145. };
  146. */
  147. /**
  148. * ?
  149. */
  150. $$.fn.instance = function(property) {
  151. return this.map(function() {
  152. return this[property];
  153. });
  154. };
  155. /**
  156. * ?
  157. */
  158. $$.fn.filter = function(selector) {
  159. return $$([].filter.call(this, function(element) {
  160. return element.parentNode && $$.query(element.parentNode, selector).indexOf(element) >= 0;
  161. }));
  162. };
  163. function _compact(array) {
  164. return array.filter(function(item) {
  165. return item !== undefined && item !== null
  166. });
  167. }
  168. function _flatten(array) {
  169. return array.length > 0 ? [].concat.apply([], array) : array
  170. }
  171. })(Quo);
  172. (function($$) {
  173. /**
  174. * ?
  175. */
  176. $$.fn.attr = function(name, value) {
  177. if ($$.toType(name) === 'string' && value === undefined) {
  178. return this[0].getAttribute(name);
  179. } else {
  180. return this.each(function() {
  181. this.setAttribute(name, value);
  182. });
  183. }
  184. };
  185. /**
  186. * ?
  187. */
  188. $$.fn.data = function(name, value) {
  189. return this.attr('data-' + name, value);
  190. };
  191. /**
  192. * ?
  193. */
  194. $$.fn.val = function(value) {
  195. if ($$.toType(value) === 'string') {
  196. return this.each(function() {
  197. this.value = value;
  198. });
  199. } else {
  200. return (this.length > 0 ? this[0].value : null)
  201. }
  202. };
  203. /**
  204. * ?
  205. */
  206. $$.fn.show = function() {
  207. return this.style("display", "block")
  208. };
  209. /**
  210. * ?
  211. */
  212. $$.fn.hide = function() {
  213. return this.style("display", "none")
  214. };
  215. /**
  216. * ?
  217. */
  218. $$.fn.height = function() {
  219. var offset = this.offset();
  220. return offset.height;
  221. };
  222. /**
  223. * ?
  224. */
  225. $$.fn.width = function() {
  226. var offset = this.offset();
  227. return offset.width;
  228. };
  229. /**
  230. * ?
  231. */
  232. $$.fn.offset = function() {
  233. var bounding = this[0].getBoundingClientRect();
  234. return {
  235. left: bounding.left + window.pageXOffset,
  236. top: bounding.top + window.pageYOffset,
  237. width: bounding.width,
  238. height: bounding.height
  239. };
  240. };
  241. /**
  242. * ?
  243. */
  244. $$.fn.remove = function() {
  245. return this.each(function() {
  246. if (this.parentNode != null) {
  247. this.parentNode.removeChild(this);
  248. }
  249. });
  250. };
  251. })(Quo);
  252. (function($$) {
  253. var IS_WEBKIT = /WebKit\/([\d.]+)/;
  254. var SUPPORTED_OS = {
  255. android: /(Android)\s+([\d.]+)/,
  256. ipad: /(iPad).*OS\s([\d_]+)/,
  257. iphone: /(iPhone\sOS)\s([\d_]+)/,
  258. blackberry: /(BlackBerry).*Version\/([\d.]+)/,
  259. webos: /(webOS|hpwOS)[\s\/]([\d.]+)/
  260. };
  261. var CURRENT_ENVIRONMENT = null;
  262. /**
  263. * ?
  264. */
  265. $$.isMobile = function() {
  266. CURRENT_ENVIRONMENT = CURRENT_ENVIRONMENT || _detectEnvironment();
  267. return CURRENT_ENVIRONMENT.isMobile;
  268. };
  269. /**
  270. * ?
  271. */
  272. $$.environment = function() {
  273. CURRENT_ENVIRONMENT = CURRENT_ENVIRONMENT || _detectEnvironment();
  274. return CURRENT_ENVIRONMENT;
  275. };
  276. /**
  277. * ?
  278. */
  279. $$.isOnline = function() {
  280. return (navigator.onLine);
  281. };
  282. var _detectEnvironment = function() {
  283. var user_agent = navigator.userAgent;
  284. var environment = {};
  285. environment.browser = _detectBrowser(user_agent);
  286. environment.os = _detectOS(user_agent);
  287. environment.isMobile = (environment.os) ? true : false;
  288. return environment;
  289. }
  290. var _detectBrowser = function(user_agent) {
  291. var is_webkit = user_agent.match(IS_WEBKIT);
  292. return (is_webkit) ? is_webkit[0]: user_agent;
  293. }
  294. var _detectOS = function(user_agent) {
  295. var detected_os;
  296. for (os in SUPPORTED_OS) {
  297. var supported = user_agent.match(SUPPORTED_OS[os]);
  298. if (supported) {
  299. detected_os = {
  300. name: (os === 'iphone' || os === 'ipad') ? 'ios' : os,
  301. version: supported[2].replace('_', '.')
  302. }
  303. break;
  304. }
  305. }
  306. return detected_os;
  307. }
  308. })(Quo);
  309. (function($$) {
  310. /**
  311. * ?
  312. */
  313. $$.fn.text = function(value) {
  314. return (!value) ?
  315. this[0].textContent
  316. :
  317. this.each(function() {
  318. this.textContent = value;
  319. });
  320. };
  321. /**
  322. * ?
  323. */
  324. $$.fn.html = function(value) {
  325. return ($$.toType('value') === 'string') ?
  326. this.each(function() {
  327. this.innerHTML = value;
  328. })
  329. :
  330. this[0].innerHTML;
  331. };
  332. /**
  333. * ?
  334. */
  335. $$.fn.append = function(value) {
  336. return this.each(function() {
  337. if ($$.toType(value) === 'string') {
  338. if (value) {
  339. var div = document.createElement();
  340. div.innerHTML = value;
  341. this.appendChild(div.firstChild);
  342. }
  343. } else {
  344. this.insertBefore(value);
  345. }
  346. });
  347. };
  348. /**
  349. * ?
  350. */
  351. $$.fn.prepend = function(value) {
  352. return this.each(function() {
  353. if ($$.toType(value) === 'string') {
  354. this.innerHTML = value + this.innerHTML;
  355. } else {
  356. var parent = this.parentNode;
  357. parent.insertBefore(value, parent.firstChild);
  358. }
  359. });
  360. };
  361. /**
  362. * ?
  363. */
  364. $$.fn.empty = function() {
  365. return this.each(function() {
  366. this.innerHTML = null;
  367. });
  368. };
  369. })(Quo);
  370. (function($$){
  371. var PARENT_NODE = 'parentNode';
  372. /**
  373. * ?
  374. */
  375. $$.query = function(domain, selector) {
  376. var dom_elements = document.querySelectorAll(selector);
  377. dom_elements = Array.prototype.slice.call(dom_elements);
  378. return dom_elements;
  379. };
  380. /**
  381. * ?
  382. */
  383. $$.fn.parent = function(selector) {
  384. var ancestors = (selector) ? _findAncestors(this) : this.instance(PARENT_NODE);
  385. return _filtered(ancestors, selector);
  386. };
  387. /**
  388. * ?
  389. */
  390. $$.fn.siblings = function(selector) {
  391. var siblings_elements = this.map(function(index, element) {
  392. return Array.prototype.slice.call(element.parentNode.children).filter(function(child) {
  393. return child !== element
  394. });
  395. });
  396. return _filtered(siblings_elements, selector);
  397. };
  398. /**
  399. * ?
  400. */
  401. $$.fn.children = function(selector) {
  402. var children_elements = this.map(function() {
  403. return Array.prototype.slice.call(this.children);
  404. });
  405. return _filtered(children_elements, selector);
  406. };
  407. /**
  408. * ?
  409. */
  410. $$.fn.get = function(index) {
  411. return index === undefined ? this : this[index]
  412. };
  413. /**
  414. * ?
  415. */
  416. $$.fn.first = function() {
  417. return $$(this[0]);
  418. };
  419. /**
  420. * ?
  421. */
  422. $$.fn.last = function() {
  423. var last_element_index = this.length - 1;
  424. return $$(this[last_element_index]);
  425. };
  426. /**
  427. * ?
  428. */
  429. $$.fn.closest = function(selector, context) {
  430. var node = this[0];
  431. var candidates = $$(selector);
  432. if (!candidates.length) node = null;
  433. while (node && candidates.indexOf(node) < 0) {
  434. node = node !== context && node !== document && node.parentNode;
  435. }
  436. return $$(node);
  437. };
  438. /**
  439. * ?
  440. */
  441. $$.fn.each = function(callback) {
  442. this.forEach( function(el, idx) {
  443. callback.call(el, idx, el)
  444. });
  445. return this;
  446. };
  447. var _findAncestors = function(nodes) {
  448. var ancestors = []
  449. while (nodes.length > 0) {
  450. nodes = $$.map(nodes, function(node) {
  451. if ((node = node.parentNode) && node !== document && ancestors.indexOf(node) < 0) {
  452. ancestors.push(node);
  453. return node;
  454. }
  455. });
  456. }
  457. return ancestors;
  458. }
  459. var _filtered = function(nodes, selector) {
  460. return (selector === undefined) ? $$(nodes) : $$(nodes).filter(selector);
  461. }
  462. })(Quo);
  463. (function($){
  464. /**
  465. * ?
  466. */
  467. $.fn.addClass = function(name) {
  468. return this.each(function() {
  469. if (!_existsClass(name, this.className)) {
  470. this.className += ' ' + name;
  471. }
  472. });
  473. };
  474. /**
  475. * ?
  476. */
  477. $.fn.removeClass = function(name) {
  478. return this.each(function() {
  479. if (_existsClass(name, this.className)) {
  480. this.className = this.className.replace(name, ' ');
  481. }
  482. });
  483. };
  484. /**
  485. * ?
  486. */
  487. $.fn.toggleClass = function(name) {
  488. return this.each(function() {
  489. if (_existsClass(name, this.className)) {
  490. this.className = this.className.replace(name, ' ');
  491. } else {
  492. this.className += ' ' + name;
  493. }
  494. });
  495. };
  496. /**
  497. * ?
  498. */
  499. $.fn.hasClass = function(name) {
  500. return _existsClass(name, this[0].className);
  501. }
  502. /**
  503. * ?
  504. */
  505. $.fn.style = function(property, value) {
  506. return (!value) ?
  507. this[0].style[property] || _computedStyle(this[0], property)
  508. :
  509. this.each(function() {
  510. this.style[property] = value;
  511. });
  512. };
  513. function _existsClass(name, className) {
  514. var classes = className.split(/\s+/g);
  515. return (classes.indexOf(name) >= 0);
  516. }
  517. function _computedStyle(element, property) {
  518. return document.defaultView.getComputedStyle(element, '')[property];
  519. }
  520. })(Quo);
  521. (function($$) {
  522. var DEFAULT = { TYPE: 'GET', MIME: 'json' };
  523. var MIME_TYPES = {
  524. script: 'text/javascript, application/javascript',
  525. json: 'application/json',
  526. xml: 'application/xml, text/xml',
  527. html: 'text/html',
  528. text: 'text/plain'
  529. };
  530. var JSONP_ID = 0;
  531. /**
  532. * ?
  533. */
  534. $$.ajaxSettings = {
  535. type: DEFAULT.TYPE,
  536. async: true,
  537. success: {},
  538. error: {},
  539. context: null,
  540. dataType: DEFAULT.MIME,
  541. headers: {},
  542. xhr: function () {
  543. return new window.XMLHttpRequest();
  544. },
  545. crossDomain: false,
  546. timeout: 0
  547. };
  548. /**
  549. * ?
  550. */
  551. $$.ajax = function(options) {
  552. var settings = $$.mix($$.ajaxSettings, options);
  553. if (_isJsonP(settings.url)) return $$.jsonp(settings);
  554. var xhr = settings.xhr();
  555. xhr.onreadystatechange = function() {
  556. if (xhr.readyState === 4) {
  557. clearTimeout(abortTimeout);
  558. _xhrStatus(xhr, settings);
  559. }
  560. }
  561. xhr.open(settings.type, settings.url, settings.async);
  562. _xhrHeaders(xhr, settings);
  563. if (settings.timeout > 0) {
  564. var abortTimeout = setTimeout(function() {
  565. _xhrTimeout(xhr, settings);
  566. }, settings.timeout);
  567. }
  568. xhr.send(settings.data);
  569. return (settings.async) ? xhr : _parseResponse(xhr, settings);
  570. };
  571. /**
  572. * ?
  573. */
  574. $$.jsonp = function(settings) {
  575. var callbackName = 'jsonp' + (++JSONP_ID);
  576. var script = document.createElement('script');
  577. var xhr = {
  578. abort: function() {
  579. $$(script).remove();
  580. if (callbackName in window) window[callbackName] = {};
  581. }
  582. };
  583. var abortTimeout;
  584. window[callbackName] = function(response) {
  585. clearTimeout(abortTimeout);
  586. $$(script).remove();
  587. delete window[callbackName];
  588. _xhrSuccess(response, xhr, settings);
  589. };
  590. script.src = settings.url.replace(/=\?/, '=' + callbackName);
  591. $$('head').append(script);
  592. if (settings.timeout > 0) {
  593. abortTimeout = setTimeout(function() {
  594. _xhrTimeout(xhr, settings);
  595. }, settings.timeout);
  596. }
  597. return xhr;
  598. };
  599. /**
  600. * ?
  601. */
  602. $$.get = function(url, data, success, dataType) {
  603. url += _serializeParameters(data);
  604. return $$.ajax({
  605. url: url,
  606. success: success,
  607. dataType: dataType
  608. });
  609. };
  610. /**
  611. * ?
  612. */
  613. $$.post = function(url, data, success, dataType) {
  614. return $$.ajax({
  615. type: 'POST',
  616. url: url,
  617. data: data,
  618. success: success,
  619. dataType: dataType,
  620. contentType: 'application/x-www-form-urlencoded'
  621. });
  622. };
  623. /**
  624. * ?
  625. */
  626. $$.json = function(url, data, success) {
  627. url += _serializeParameters(data);
  628. return $$.ajax({
  629. url: url,
  630. success: success,
  631. dataType: DEFAULT.MIME
  632. });
  633. };
  634. function _xhrStatus(xhr, settings) {
  635. if (xhr.status === 200 || xhr.status === 0) {
  636. if (settings.async) {
  637. var response = _parseResponse(xhr, settings);
  638. _xhrSuccess(response, xhr, settings);
  639. }
  640. } else {
  641. _xhrError('QuoJS » $$.ajax', xhr, settings);
  642. }
  643. }
  644. function _xhrSuccess(response, xhr, settings) {
  645. settings.success.call(settings.context, response, xhr);
  646. }
  647. function _xhrError(type, xhr, settings) {
  648. settings.error.call(settings.context, type, xhr, settings);
  649. }
  650. function _xhrHeaders(xhr, settings) {
  651. if (settings.contentType) settings.headers['Content-Type'] = settings.contentType;
  652. if (settings.dataType) settings.headers['Accept'] = MIME_TYPES[settings.dataType];
  653. for (header in settings.headers) {
  654. xhr.setRequestHeader(header, settings.headers[header]);
  655. }
  656. }
  657. function _xhrTimeout(xhr, settings) {
  658. xhr.onreadystatechange = {};
  659. xhr.abort();
  660. _xhrError('QuoJS » $$.ajax : timeout exceeded', xhr, settings);
  661. }
  662. function _parseResponse(xhr, settings) {
  663. var response = xhr.responseText;
  664. if (response) {
  665. if (settings.dataType === DEFAULT.MIME) {
  666. try {
  667. response = JSON.parse(response);
  668. }
  669. catch (error) {
  670. response = error;
  671. _xhrError('Parse Error', xhr, settings);
  672. }
  673. } else if (settings.dataType === 'xml') {
  674. response = xhr.responseXML;
  675. }
  676. }
  677. return response;
  678. }
  679. var _serializeParameters = function(parameters) {
  680. var serialize = '?';
  681. for (var parameter in parameters) {
  682. if (parameters.hasOwnProperty(parameter)) {
  683. if (serialize !== '?') serialize += '&';
  684. serialize += parameter + '=' + parameters[parameter];
  685. }
  686. }
  687. return (serialize === '?') ? '' : serialize;
  688. }
  689. var _isJsonP = function(url) {
  690. return (/=\?/.test(url));
  691. }
  692. })(Quo);
  693. (function($$) {
  694. var SHORTCUTS = [
  695. 'touch',
  696. 'tap' ];
  697. var SHORTCUTS_EVENTS = {
  698. touch: 'touchstart',
  699. tap: 'tap' };
  700. var READY_EXPRESSION = /complete|loaded|interactive/;
  701. /**
  702. * ?
  703. */
  704. SHORTCUTS.forEach(function(event) {
  705. $$.fn[event] = function(callback) {
  706. $$(document.body).delegate(this.selector, SHORTCUTS_EVENTS[event], callback);
  707. return this;
  708. };
  709. });
  710. /**
  711. * ?
  712. */
  713. $$.fn.on = function(event, selector, callback) {
  714. return (selector === undefined || $$.toType(selector) === 'function') ?
  715. this.bind(event, selector)
  716. :
  717. this.delegate(selector, event, callback);
  718. };
  719. /**
  720. * ?
  721. */
  722. $$.fn.off = function(event, selector, callback){
  723. return (selector === undefined || $$.toType(selector) === 'function') ?
  724. this.unbind(event, selector)
  725. :
  726. this.undelegate(selector, event, callback);
  727. };
  728. /**
  729. * ?
  730. */
  731. $$.fn.ready = function(callback) {
  732. if (READY_EXPRESSION.test(document.readyState)) {
  733. callback($$);
  734. }
  735. else {
  736. document.addEventListener('DOMContentLoaded', function(){ callback($$) }, false);
  737. }
  738. return this;
  739. };
  740. })(Quo);
  741. (function($$) {
  742. var ELEMENT_ID = 1;
  743. var HANDLERS = {};
  744. var EVENT_METHODS = {
  745. preventDefault: 'isDefaultPrevented',
  746. stopImmediatePropagation: 'isImmediatePropagationStopped',
  747. stopPropagation: 'isPropagationStopped' };
  748. var EVENTS_DESKTOP = {
  749. touchstart : 'mousedown',
  750. touchmove: 'mousemove',
  751. touchend: 'mouseup',
  752. tap: 'click',
  753. doubletap: 'dblclick',
  754. orientationchange: 'resize' };
  755. /**
  756. * ?
  757. */
  758. $$.Event = function(type, props) {
  759. var event = document.createEvent('Events');
  760. event.initEvent(type, true, true, null, null, null, null, null, null, null, null, null, null, null, null);
  761. return event;
  762. };
  763. /**
  764. * ?
  765. */
  766. $$.fn.bind = function(event, callback) {
  767. return this.each(function() {
  768. _subscribe(this, event, callback);
  769. });
  770. };
  771. /**
  772. * ?
  773. */
  774. $$.fn.unbind = function(event, callback){
  775. return this.each(function() {
  776. _unsubscribe(this, event, callback);
  777. });
  778. };
  779. /**
  780. * ?
  781. */
  782. $$.fn.delegate = function(selector, event, callback) {
  783. return this.each(function(i, element) {
  784. _subscribe(element, event, callback, selector, function(fn) {
  785. return function(e) {
  786. var match = $$(e.target).closest(selector, element).get(0);
  787. if (match) {
  788. var evt = $$.extend(_createProxy(e), {
  789. currentTarget: match,
  790. liveFired: element
  791. });
  792. return fn.apply(match, [evt].concat([].slice.call(arguments, 1)));
  793. }
  794. }
  795. });
  796. });
  797. };
  798. /**
  799. * ?
  800. */
  801. $$.fn.undelegate = function(selector, event, callback){
  802. return this.each(function(){
  803. _unsubscribe(this, event, callback, selector);
  804. });
  805. };
  806. /**
  807. * ?
  808. */
  809. $$.fn.trigger = function(event) {
  810. if ($$.toType(event) === 'string') event = $$.Event(event);
  811. return this.each(function() {
  812. this.dispatchEvent(event);
  813. });
  814. };
  815. function _subscribe(element, event, callback, selector, delegate_callback) {
  816. event = _environmentEvent(event);
  817. var element_id = _getElementId(element);
  818. var element_handlers = HANDLERS[element_id] || (HANDLERS[element_id] = []);
  819. var delegate = delegate_callback && delegate_callback(callback, event);
  820. var handler = {
  821. event: event,
  822. callback: callback,
  823. selector: selector,
  824. proxy: _createProxyCallback(delegate, callback, element),
  825. delegate: delegate,
  826. index: element_handlers.length
  827. };
  828. element_handlers.push(handler);
  829. element.addEventListener(handler.event, handler.proxy, false);
  830. }
  831. function _unsubscribe(element, event, callback, selector) {
  832. event = _environmentEvent(event);
  833. var element_id = _getElementId(element);
  834. _findHandlers(element_id, event, callback, selector).forEach(function(handler) {
  835. delete HANDLERS[element_id][handler.index];
  836. element.removeEventListener(handler.event, handler.proxy, false);
  837. });
  838. }
  839. function _getElementId(element) {
  840. return element._id || (element._id = ELEMENT_ID++);
  841. }
  842. function _environmentEvent(event) {
  843. var environment_event = ($$.isMobile()) ? event : EVENTS_DESKTOP[event];
  844. return (environment_event) || event;
  845. }
  846. function _createProxyCallback(delegate, callback, element) {
  847. var callback = delegate || callback;
  848. var proxy = function (event) {
  849. var result = callback.apply(element, [event].concat(event.data));
  850. if (result === false) {
  851. event.preventDefault();
  852. }
  853. return result;
  854. };
  855. return proxy;
  856. }
  857. function _findHandlers(element_id, event, fn, selector) {
  858. return (HANDLERS[element_id] || []).filter(function(handler) {
  859. return handler
  860. && (!event || handler.event == event)
  861. && (!fn || handler.fn == fn)
  862. && (!selector || handler.selector == selector);
  863. });
  864. }
  865. function _createProxy(event) {
  866. var proxy = $$.extend({originalEvent: event}, event);
  867. $$.each(EVENT_METHODS, function(name, method) {
  868. proxy[name] = function() {
  869. this[method] = function(){ return true };
  870. return event[name].apply(event, arguments);
  871. };
  872. proxy[method] = function() { return false };
  873. })
  874. return proxy;
  875. }
  876. })(Quo);
  877. (function($$) {
  878. var TOUCH = {};
  879. var TOUCH_TIMEOUT;
  880. var LONGTAP_DELAY = 750;
  881. var GESTURES = ['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'longTap'];
  882. /**
  883. * ?
  884. */
  885. GESTURES.forEach(function(event) {
  886. $$.fn[event] = function(callback) {
  887. return this.on(event, callback);
  888. };
  889. });
  890. /**
  891. * ?
  892. */
  893. $$(document).ready(function() {
  894. _listenTouches();
  895. });
  896. function _listenTouches() {
  897. var environment = $$(document.body);
  898. environment.bind('touchstart', _onTouchStart);
  899. environment.bind('touchmove', _onTouchMove);
  900. environment.bind('touchend', _onTouchEnd);
  901. environment.bind('touchcancel', _onTouchCancel);
  902. }
  903. function _onTouchStart(event) {
  904. var now = Date.now();
  905. var delta = now - (TOUCH.last || now);
  906. var first_touch = ($$.isMobile()) ? event.touches[0] : event;
  907. TOUCH_TIMEOUT && clearTimeout(TOUCH_TIMEOUT);
  908. TOUCH = {
  909. el: $$(_parentIfText(first_touch.target)),
  910. x1: first_touch.pageX,
  911. y1: first_touch.pageY,
  912. isDoubleTap: (delta > 0 && delta <= 250) ? true : false,
  913. last: now
  914. }
  915. setTimeout(_longTap, LONGTAP_DELAY);
  916. }
  917. function _onTouchMove(event) {
  918. var move_touch = ($$.isMobile()) ? event.touches[0] : event;
  919. TOUCH.x2 = move_touch.pageX;
  920. TOUCH.y2 = move_touch.pageY;
  921. }
  922. function _onTouchEnd(event) {
  923. if (TOUCH.isDoubleTap) {
  924. TOUCH.el.trigger('doubleTap');
  925. TOUCH = {};
  926. } else if (TOUCH.x2 > 0 || TOUCH.y2 > 0) {
  927. (Math.abs(TOUCH.x1 - TOUCH.x2) > 30 || Math.abs(TOUCH.y1 - TOUCH.y2) > 30) &&
  928. TOUCH.el.trigger('swipe') &&
  929. TOUCH.el.trigger('swipe' + (_swipeDirection(TOUCH.x1, TOUCH.x2, TOUCH.y1, TOUCH.y2)));
  930. TOUCH.x1 = TOUCH.x2 = TOUCH.y1 = TOUCH.y2 = TOUCH.last = 0;
  931. TOUCH = {};
  932. } else {
  933. TOUCH.el.trigger('tap');
  934. TOUCH_TIMEOUT = setTimeout(function(){
  935. TOUCH_TIMEOUT = null;
  936. TOUCH = {};
  937. }, 250);
  938. }
  939. }
  940. function _onTouchCancel(event) {
  941. TOUCH = {};
  942. clearTimeout(TOUCH_TIMEOUT);
  943. }
  944. function _parentIfText(node) {
  945. return 'tagName' in node ? node : node.parentNode;
  946. }
  947. function _swipeDirection(x1, x2, y1, y2) {
  948. var xDelta = Math.abs(x1 - x2);
  949. var yDelta = Math.abs(y1 - y2);
  950. if (xDelta >= yDelta) {
  951. return (x1 - x2 > 0 ? 'Left' : 'Right');
  952. } else {
  953. return (y1 - y2 > 0 ? 'Up' : 'Down');
  954. }
  955. }
  956. function _longTap() {
  957. if (TOUCH.last && (Date.now() - TOUCH.last >= LONGTAP_DELAY)) {
  958. TOUCH.el.trigger('longTap');
  959. TOUCH = {};
  960. }
  961. }
  962. })(Quo);