QuoJS.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143
  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. var div = document.createElement();
  339. div.innerHTML = value;
  340. this.appendChild(div.firstChild);
  341. } else {
  342. this.parentNode.insertBefore(value);
  343. }
  344. });
  345. };
  346. /**
  347. * ?
  348. */
  349. $$.fn.prepend = function(value) {
  350. return this.each(function() {
  351. if ($$.toType(value) === 'string') {
  352. this.innerHTML = value + this.innerHTML;
  353. } else {
  354. var parent = this.parentNode;
  355. parent.insertBefore(value, parent.firstChild);
  356. }
  357. });
  358. };
  359. /**
  360. * ?
  361. */
  362. $$.fn.empty = function() {
  363. return this.each(function() {
  364. this.innerHTML = null;
  365. });
  366. };
  367. })(Quo);
  368. (function($$){
  369. var PARENT_NODE = 'parentNode';
  370. /**
  371. * ?
  372. */
  373. $$.query = function(domain, selector) {
  374. var dom_elements = document.querySelectorAll(selector);
  375. dom_elements = Array.prototype.slice.call(dom_elements);
  376. return dom_elements;
  377. };
  378. /**
  379. * ?
  380. */
  381. $$.fn.parent = function(selector) {
  382. var ancestors = (selector) ? _findAncestors(this) : this.instance(PARENT_NODE);
  383. return _filtered(ancestors, selector);
  384. };
  385. /**
  386. * ?
  387. */
  388. $$.fn.siblings = function(selector) {
  389. var siblings_elements = this.map(function(index, element) {
  390. return Array.prototype.slice.call(element.parentNode.children).filter(function(child) {
  391. return child !== element
  392. });
  393. });
  394. return _filtered(siblings_elements, selector);
  395. };
  396. /**
  397. * ?
  398. */
  399. $$.fn.children = function(selector) {
  400. var children_elements = this.map(function() {
  401. return Array.prototype.slice.call(this.children);
  402. });
  403. return _filtered(children_elements, selector);
  404. };
  405. /**
  406. * ?
  407. */
  408. $$.fn.get = function(index) {
  409. return index === undefined ? this : this[index]
  410. };
  411. /**
  412. * ?
  413. */
  414. $$.fn.first = function() {
  415. return $$(this[0]);
  416. };
  417. /**
  418. * ?
  419. */
  420. $$.fn.last = function() {
  421. var last_element_index = this.length - 1;
  422. return $$(this[last_element_index]);
  423. };
  424. /**
  425. * ?
  426. */
  427. $$.fn.closest = function(selector, context) {
  428. var node = this[0];
  429. var candidates = $$(selector);
  430. if (!candidates.length) node = null;
  431. while (node && candidates.indexOf(node) < 0) {
  432. node = node !== context && node !== document && node.parentNode;
  433. }
  434. return $$(node);
  435. };
  436. /**
  437. * ?
  438. */
  439. $$.fn.each = function(callback) {
  440. this.forEach( function(el, idx) {
  441. callback.call(el, idx, el)
  442. });
  443. return this;
  444. };
  445. var _findAncestors = function(nodes) {
  446. var ancestors = []
  447. while (nodes.length > 0) {
  448. nodes = $$.map(nodes, function(node) {
  449. if ((node = node.parentNode) && node !== document && ancestors.indexOf(node) < 0) {
  450. ancestors.push(node);
  451. return node;
  452. }
  453. });
  454. }
  455. return ancestors;
  456. }
  457. var _filtered = function(nodes, selector) {
  458. return (selector === undefined) ? $$(nodes) : $$(nodes).filter(selector);
  459. }
  460. })(Quo);
  461. (function($){
  462. /**
  463. * ?
  464. */
  465. $.fn.addClass = function(name) {
  466. return this.each(function() {
  467. if (!_existsClass(name, this.className)) {
  468. this.className += ' ' + name;
  469. }
  470. });
  471. };
  472. /**
  473. * ?
  474. */
  475. $.fn.removeClass = function(name) {
  476. return this.each(function() {
  477. if (_existsClass(name, this.className)) {
  478. this.className = this.className.replace(name, ' ');
  479. }
  480. });
  481. };
  482. /**
  483. * ?
  484. */
  485. $.fn.toggleClass = function(name) {
  486. return this.each(function() {
  487. if (_existsClass(name, this.className)) {
  488. this.className = this.className.replace(name, ' ');
  489. } else {
  490. this.className += ' ' + name;
  491. }
  492. });
  493. };
  494. /**
  495. * ?
  496. */
  497. $.fn.hasClass = function(name) {
  498. return _existsClass(name, this[0].className);
  499. }
  500. /**
  501. * ?
  502. */
  503. $.fn.style = function(property, value) {
  504. return (!value) ?
  505. this[0].style[property] || _computedStyle(this[0], property)
  506. :
  507. this.each(function() {
  508. this.style[property] = value;
  509. });
  510. };
  511. function _existsClass(name, className) {
  512. var classes = className.split(/\s+/g);
  513. return (classes.indexOf(name) >= 0);
  514. }
  515. function _computedStyle(element, property) {
  516. return document.defaultView.getComputedStyle(element, '')[property];
  517. }
  518. })(Quo);
  519. (function($$) {
  520. var DEFAULT = { TYPE: 'GET', MIME: 'json' };
  521. var MIME_TYPES = {
  522. script: 'text/javascript, application/javascript',
  523. json: 'application/json',
  524. xml: 'application/xml, text/xml',
  525. html: 'text/html',
  526. text: 'text/plain'
  527. };
  528. var JSONP_ID = 0;
  529. /**
  530. * ?
  531. */
  532. $$.ajaxSettings = {
  533. type: DEFAULT.TYPE,
  534. async: true,
  535. success: {},
  536. error: {},
  537. context: null,
  538. dataType: DEFAULT.MIME,
  539. headers: {},
  540. xhr: function () {
  541. return new window.XMLHttpRequest();
  542. },
  543. crossDomain: false,
  544. timeout: 0
  545. };
  546. /**
  547. * ?
  548. */
  549. $$.ajax = function(options) {
  550. var settings = $$.mix($$.ajaxSettings, options);
  551. if (_isJsonP(settings.url)) return $$.jsonp(settings);
  552. var xhr = settings.xhr();
  553. xhr.onreadystatechange = function() {
  554. if (xhr.readyState === 4) {
  555. clearTimeout(abortTimeout);
  556. _xhrStatus(xhr, settings);
  557. }
  558. }
  559. xhr.open(settings.type, settings.url, settings.async);
  560. _xhrHeaders(xhr, settings);
  561. if (settings.timeout > 0) {
  562. var abortTimeout = setTimeout(function() {
  563. _xhrTimeout(xhr, settings);
  564. }, settings.timeout);
  565. }
  566. try {
  567. xhr.send(settings.data);
  568. if (xhr.status !== 500) {
  569. return (settings.async) ? xhr : _parseResponse(xhr, settings);
  570. }
  571. }
  572. catch (error) {
  573. xhr = error;
  574. _xhrError('Resource not found', xhr, settings);
  575. }
  576. };
  577. /**
  578. * ?
  579. */
  580. $$.jsonp = function(settings) {
  581. var callbackName = 'jsonp' + (++JSONP_ID);
  582. var script = document.createElement('script');
  583. var xhr = {
  584. abort: function() {
  585. $$(script).remove();
  586. if (callbackName in window) window[callbackName] = {};
  587. }
  588. };
  589. var abortTimeout;
  590. window[callbackName] = function(response) {
  591. clearTimeout(abortTimeout);
  592. $$(script).remove();
  593. delete window[callbackName];
  594. _xhrSuccess(response, xhr, settings);
  595. };
  596. script.src = settings.url.replace(/=\?/, '=' + callbackName);
  597. $$('head').append(script);
  598. if (settings.timeout > 0) {
  599. abortTimeout = setTimeout(function() {
  600. _xhrTimeout(xhr, settings);
  601. }, settings.timeout);
  602. }
  603. return xhr;
  604. };
  605. /**
  606. * ?
  607. */
  608. $$.get = function(url, data, success, dataType) {
  609. url += _serializeParameters(data);
  610. return $$.ajax({
  611. url: url,
  612. success: success,
  613. dataType: dataType
  614. });
  615. };
  616. /**
  617. * ?
  618. */
  619. $$.post = function(url, data, success, dataType) {
  620. return $$.ajax({
  621. type: 'POST',
  622. url: url,
  623. data: data,
  624. success: success,
  625. dataType: dataType,
  626. contentType: 'application/x-www-form-urlencoded'
  627. });
  628. };
  629. /**
  630. * ?
  631. */
  632. $$.json = function(url, data, success) {
  633. url += _serializeParameters(data);
  634. return $$.ajax({
  635. url: url,
  636. success: success,
  637. dataType: DEFAULT.MIME
  638. });
  639. };
  640. function _xhrStatus(xhr, settings) {
  641. if (xhr.status === 200 || xhr.status === 0) {
  642. if (settings.async) {
  643. var response = _parseResponse(xhr, settings);
  644. _xhrSuccess(response, xhr, settings);
  645. }
  646. } else {
  647. _xhrError('QuoJS » $$.ajax', xhr, settings);
  648. }
  649. }
  650. function _xhrSuccess(response, xhr, settings) {
  651. settings.success.call(settings.context, response, xhr);
  652. }
  653. function _xhrError(type, xhr, settings) {
  654. settings.error.call(settings.context, type, xhr, settings);
  655. }
  656. function _xhrHeaders(xhr, settings) {
  657. if (settings.contentType) settings.headers['Content-Type'] = settings.contentType;
  658. if (settings.dataType) settings.headers['Accept'] = MIME_TYPES[settings.dataType];
  659. for (header in settings.headers) {
  660. xhr.setRequestHeader(header, settings.headers[header]);
  661. }
  662. }
  663. function _xhrTimeout(xhr, settings) {
  664. xhr.onreadystatechange = {};
  665. xhr.abort();
  666. _xhrError('QuoJS » $$.ajax : timeout exceeded', xhr, settings);
  667. }
  668. function _parseResponse(xhr, settings) {
  669. var response = xhr.responseText;
  670. if (response) {
  671. if (settings.dataType === DEFAULT.MIME) {
  672. try {
  673. response = JSON.parse(response);
  674. }
  675. catch (error) {
  676. response = error;
  677. _xhrError('Parse Error', xhr, settings);
  678. }
  679. }
  680. }
  681. return response;
  682. }
  683. var _serializeParameters = function(parameters) {
  684. var serialize = '?';
  685. for (var parameter in parameters) {
  686. if (parameters.hasOwnProperty(parameter)) {
  687. if (serialize !== '?') serialize += '&';
  688. serialize += parameter + '=' + parameters[parameter];
  689. }
  690. }
  691. return (serialize === '?') ? '' : serialize;
  692. }
  693. var _isJsonP = function(url) {
  694. return (/=\?/.test(url));
  695. }
  696. })(Quo);
  697. (function($$) {
  698. var SHORTCUTS = [
  699. 'touch',
  700. 'tap' ];
  701. var SHORTCUTS_EVENTS = {
  702. touch: 'touchstart',
  703. tap: 'tap' };
  704. var READY_EXPRESSION = /complete|loaded|interactive/;
  705. /**
  706. * ?
  707. */
  708. SHORTCUTS.forEach(function(event) {
  709. $$.fn[event] = function(callback) {
  710. $$(document.body).delegate(this.selector, SHORTCUTS_EVENTS[event], callback);
  711. return this;
  712. };
  713. });
  714. /**
  715. * ?
  716. */
  717. $$.fn.on = function(event, selector, callback) {
  718. return (selector === undefined || $$.toType(selector) === 'function') ?
  719. this.bind(event, selector)
  720. :
  721. this.delegate(selector, event, callback);
  722. };
  723. /**
  724. * ?
  725. */
  726. $$.fn.off = function(event, selector, callback){
  727. return (selector === undefined || $$.toType(selector) === 'function') ?
  728. this.unbind(event, selector)
  729. :
  730. this.undelegate(selector, event, callback);
  731. };
  732. /**
  733. * ?
  734. */
  735. $$.fn.ready = function(callback) {
  736. if (READY_EXPRESSION.test(document.readyState)) {
  737. callback($$);
  738. }
  739. else {
  740. document.addEventListener('DOMContentLoaded', function(){ callback($$) }, false);
  741. }
  742. return this;
  743. };
  744. })(Quo);
  745. (function($$) {
  746. var ELEMENT_ID = 1;
  747. var HANDLERS = {};
  748. var EVENT_METHODS = {
  749. preventDefault: 'isDefaultPrevented',
  750. stopImmediatePropagation: 'isImmediatePropagationStopped',
  751. stopPropagation: 'isPropagationStopped' };
  752. var EVENTS_DESKTOP = {
  753. touchstart : 'mousedown',
  754. touchmove: 'mousemove',
  755. touchend: 'mouseup',
  756. tap: 'click',
  757. doubletap: 'dblclick',
  758. orientationchange: 'resize' };
  759. /**
  760. * ?
  761. */
  762. $$.Event = function(type, props) {
  763. var event = document.createEvent('Events');
  764. event.initEvent(type, true, true, null, null, null, null, null, null, null, null, null, null, null, null);
  765. return event;
  766. };
  767. /**
  768. * ?
  769. */
  770. $$.fn.bind = function(event, callback) {
  771. return this.each(function() {
  772. _subscribe(this, event, callback);
  773. });
  774. };
  775. /**
  776. * ?
  777. */
  778. $$.fn.unbind = function(event, callback){
  779. return this.each(function() {
  780. _unsubscribe(this, event, callback);
  781. });
  782. };
  783. /**
  784. * ?
  785. */
  786. $$.fn.delegate = function(selector, event, callback) {
  787. return this.each(function(i, element) {
  788. _subscribe(element, event, callback, selector, function(fn) {
  789. return function(e) {
  790. var match = $$(e.target).closest(selector, element).get(0);
  791. if (match) {
  792. var evt = $$.extend(_createProxy(e), {
  793. currentTarget: match,
  794. liveFired: element
  795. });
  796. return fn.apply(match, [evt].concat([].slice.call(arguments, 1)));
  797. }
  798. }
  799. });
  800. });
  801. };
  802. /**
  803. * ?
  804. */
  805. $$.fn.undelegate = function(selector, event, callback){
  806. return this.each(function(){
  807. _unsubscribe(this, event, callback, selector);
  808. });
  809. };
  810. /**
  811. * ?
  812. */
  813. $$.fn.trigger = function(event) {
  814. if ($$.toType(event) === 'string') event = $$.Event(event);
  815. return this.each(function() {
  816. this.dispatchEvent(event);
  817. });
  818. };
  819. function _subscribe(element, event, callback, selector, delegate_callback) {
  820. event = _environmentEvent(event);
  821. var element_id = _getElementId(element);
  822. var element_handlers = HANDLERS[element_id] || (HANDLERS[element_id] = []);
  823. var delegate = delegate_callback && delegate_callback(callback, event);
  824. var handler = {
  825. event: event,
  826. callback: callback,
  827. selector: selector,
  828. proxy: _createProxyCallback(delegate, callback, element),
  829. delegate: delegate,
  830. index: element_handlers.length
  831. };
  832. element_handlers.push(handler);
  833. element.addEventListener(handler.event, handler.proxy, false);
  834. }
  835. function _unsubscribe(element, event, callback, selector) {
  836. event = _environmentEvent(event);
  837. var element_id = _getElementId(element);
  838. _findHandlers(element_id, event, callback, selector).forEach(function(handler) {
  839. delete HANDLERS[element_id][handler.index];
  840. element.removeEventListener(handler.event, handler.proxy, false);
  841. });
  842. }
  843. function _getElementId(element) {
  844. return element._id || (element._id = ELEMENT_ID++);
  845. }
  846. function _environmentEvent(event) {
  847. var environment_event = ($$.isMobile()) ? event : EVENTS_DESKTOP[event];
  848. return (environment_event) || event;
  849. }
  850. function _createProxyCallback(delegate, callback, element) {
  851. var callback = delegate || callback;
  852. var proxy = function (event) {
  853. var result = callback.apply(element, [event].concat(event.data));
  854. if (result === false) {
  855. event.preventDefault();
  856. }
  857. return result;
  858. };
  859. return proxy;
  860. }
  861. function _findHandlers(element_id, event, fn, selector) {
  862. return (HANDLERS[element_id] || []).filter(function(handler) {
  863. return handler
  864. && (!event || handler.event == event)
  865. && (!fn || handler.fn == fn)
  866. && (!selector || handler.selector == selector);
  867. });
  868. }
  869. function _createProxy(event) {
  870. var proxy = $$.extend({originalEvent: event}, event);
  871. $$.each(EVENT_METHODS, function(name, method) {
  872. proxy[name] = function() {
  873. this[method] = function(){ return true };
  874. return event[name].apply(event, arguments);
  875. };
  876. proxy[method] = function() { return false };
  877. })
  878. return proxy;
  879. }
  880. })(Quo);
  881. (function($$) {
  882. var TOUCH = {};
  883. var TOUCH_TIMEOUT;
  884. var LONGTAP_DELAY = 750;
  885. var GESTURES = ['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'longTap'];
  886. /**
  887. * ?
  888. */
  889. GESTURES.forEach(function(event) {
  890. $$.fn[event] = function(callback) {
  891. return this.on(event, callback);
  892. };
  893. });
  894. /**
  895. * ?
  896. */
  897. $$(document).ready(function() {
  898. _listenTouches();
  899. });
  900. function _listenTouches() {
  901. var environment = $$(document.body);
  902. environment.bind('touchstart', _onTouchStart);
  903. environment.bind('touchmove', _onTouchMove);
  904. environment.bind('touchend', _onTouchEnd);
  905. environment.bind('touchcancel', _onTouchCancel);
  906. }
  907. function _onTouchStart(event) {
  908. var now = Date.now();
  909. var delta = now - (TOUCH.last || now);
  910. var first_touch = ($$.isMobile()) ? event.touches[0] : event;
  911. TOUCH_TIMEOUT && clearTimeout(TOUCH_TIMEOUT);
  912. TOUCH = {
  913. el: $$(_parentIfText(first_touch.target)),
  914. x1: first_touch.pageX,
  915. y1: first_touch.pageY,
  916. isDoubleTap: (delta > 0 && delta <= 250) ? true : false,
  917. last: now
  918. }
  919. setTimeout(_longTap, LONGTAP_DELAY);
  920. }
  921. function _onTouchMove(event) {
  922. var move_touch = ($$.isMobile()) ? event.touches[0] : event;
  923. TOUCH.x2 = move_touch.pageX;
  924. TOUCH.y2 = move_touch.pageY;
  925. }
  926. function _onTouchEnd(event) {
  927. if (TOUCH.isDoubleTap) {
  928. TOUCH.el.trigger('doubleTap');
  929. TOUCH = {};
  930. } else if (TOUCH.x2 > 0 || TOUCH.y2 > 0) {
  931. (Math.abs(TOUCH.x1 - TOUCH.x2) > 30 || Math.abs(TOUCH.y1 - TOUCH.y2) > 30) &&
  932. TOUCH.el.trigger('swipe') &&
  933. TOUCH.el.trigger('swipe' + (_swipeDirection(TOUCH.x1, TOUCH.x2, TOUCH.y1, TOUCH.y2)));
  934. TOUCH.x1 = TOUCH.x2 = TOUCH.y1 = TOUCH.y2 = TOUCH.last = 0;
  935. TOUCH = {};
  936. } else {
  937. TOUCH.el.trigger('tap');
  938. TOUCH_TIMEOUT = setTimeout(function(){
  939. TOUCH_TIMEOUT = null;
  940. TOUCH = {};
  941. }, 250);
  942. }
  943. }
  944. function _onTouchCancel(event) {
  945. TOUCH = {};
  946. clearTimeout(TOUCH_TIMEOUT);
  947. }
  948. function _parentIfText(node) {
  949. return 'tagName' in node ? node : node.parentNode;
  950. }
  951. function _swipeDirection(x1, x2, y1, y2) {
  952. var xDelta = Math.abs(x1 - x2);
  953. var yDelta = Math.abs(y1 - y2);
  954. if (xDelta >= yDelta) {
  955. return (x1 - x2 > 0 ? 'Left' : 'Right');
  956. } else {
  957. return (y1 - y2 > 0 ? 'Up' : 'Down');
  958. }
  959. }
  960. function _longTap() {
  961. if (TOUCH.last && (Date.now() - TOUCH.last >= LONGTAP_DELAY)) {
  962. TOUCH.el.trigger('longTap');
  963. TOUCH = {};
  964. }
  965. }
  966. })(Quo);