iui.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. /*
  2. Copyright (c) 2007-9, iUI Project Members
  3. See LICENSE.txt for licensing terms
  4. */
  5. (function() {
  6. var slideSpeed = 20;
  7. var slideInterval = 0;
  8. var currentPage = null;
  9. var currentDialog = null;
  10. var currentWidth = 0;
  11. var currentHash = location.hash;
  12. var hashPrefix = "#_";
  13. var pageHistory = [];
  14. var newPageCount = 0;
  15. var checkTimer;
  16. var hasOrientationEvent = false;
  17. var portraitVal = "portrait";
  18. var landscapeVal = "landscape";
  19. // *************************************************************************************************
  20. window.iui =
  21. {
  22. animOn: true, // Slide animation with CSS transition is now enabled by default where supported
  23. showPage: function(page, backwards)
  24. {
  25. if (page)
  26. {
  27. if (currentDialog)
  28. {
  29. currentDialog.removeAttribute("selected");
  30. currentDialog = null;
  31. }
  32. if (hasClass(page, "dialog"))
  33. showDialog(page);
  34. else
  35. {
  36. var fromPage = currentPage;
  37. currentPage = page;
  38. if (fromPage)
  39. setTimeout(slidePages, 0, fromPage, page, backwards);
  40. else
  41. updatePage(page, fromPage);
  42. }
  43. }
  44. },
  45. showPageById: function(pageId)
  46. {
  47. var page = $(pageId);
  48. if (page)
  49. {
  50. var index = pageHistory.indexOf(pageId);
  51. var backwards = index != -1;
  52. if (backwards)
  53. pageHistory.splice(index, pageHistory.length);
  54. iui.showPage(page, backwards);
  55. }
  56. },
  57. showPageByHref: function(href, args, method, replace, cb, bw)
  58. {
  59. var req = new XMLHttpRequest();
  60. req.onerror = function()
  61. {
  62. if (cb)
  63. cb(false);
  64. };
  65. req.onreadystatechange = function()
  66. {
  67. if (req.readyState == 4)
  68. {
  69. if (replace)
  70. replaceElementWithSource(replace, req.responseText);
  71. else
  72. {
  73. var frag = document.createElement("div");
  74. frag.innerHTML = req.responseText;
  75. iui.insertPages(frag.childNodes, bw);
  76. }
  77. if (cb)
  78. setTimeout(cb, 1000, true);
  79. }
  80. };
  81. if (args)
  82. {
  83. req.open(method || "GET", href, true);
  84. req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  85. req.setRequestHeader("Content-Length", args.length);
  86. req.send(args.join("&"));
  87. }
  88. else
  89. {
  90. req.open(method || "GET", href, true);
  91. req.send(null);
  92. }
  93. },
  94. insertPages: function(nodes, bw)
  95. {
  96. var targetPage;
  97. for (var i = 0; i < nodes.length; ++i)
  98. {
  99. var child = nodes[i];
  100. if (child.nodeType == 1)
  101. {
  102. if (!child.id)
  103. child.id = "__" + (++newPageCount) + "__";
  104. var clone = $(child.id);
  105. if (clone)
  106. clone.parentNode.replaceChild(child, clone);
  107. else
  108. document.body.appendChild(child);
  109. if (child.getAttribute("selected") == "true" || !targetPage)
  110. targetPage = child;
  111. --i;
  112. }
  113. }
  114. if (targetPage)
  115. iui.showPage(targetPage, bw);
  116. },
  117. getSelectedPage: function()
  118. {
  119. for (var child = document.body.firstChild; child; child = child.nextSibling)
  120. {
  121. if (child.nodeType == 1 && child.getAttribute("selected") == "true")
  122. return child;
  123. }
  124. },
  125. isNativeUrl: function(href)
  126. {
  127. for(var i = 0; i < iui.nativeUrlPatterns.length; i++)
  128. {
  129. if(href.match(iui.nativeUrlPatterns[i])) return true;
  130. }
  131. return false;
  132. },
  133. nativeUrlPatterns: [
  134. new RegExp("^http:\/\/maps.google.com\/maps\?"),
  135. new RegExp("^mailto:"),
  136. new RegExp("^tel:"),
  137. new RegExp("^http:\/\/www.youtube.com\/watch\\?v="),
  138. new RegExp("^http:\/\/www.youtube.com\/v\/"),
  139. new RegExp("^javascript:"),
  140. ]
  141. };
  142. // *************************************************************************************************
  143. addEventListener("load", function(event)
  144. {
  145. var page = iui.getSelectedPage();
  146. var locPage = getPageFromLoc();
  147. if (page)
  148. iui.showPage(page);
  149. if (locPage && (locPage != page))
  150. iui.showPage(locPage);
  151. setTimeout(preloadImages, 0);
  152. if (typeof window.onorientationchange == "object")
  153. {
  154. window.onorientationchange=orientChangeHandler;
  155. hasOrientationEvent = true;
  156. setTimeout(orientChangeHandler, 0);
  157. }
  158. setTimeout(checkOrientAndLocation, 0);
  159. checkTimer = setInterval(checkOrientAndLocation, 300);
  160. }, false);
  161. addEventListener("unload", function(event)
  162. {
  163. return;
  164. }, false);
  165. addEventListener("click", function(event)
  166. {
  167. var link = findParent(event.target, "a");
  168. if (link)
  169. {
  170. function unselect() { link.removeAttribute("selected"); }
  171. if (link.href && link.hash && link.hash != "#" && !link.target)
  172. {
  173. link.setAttribute("selected", "true");
  174. iui.showPage($(link.hash.substr(1)));
  175. setTimeout(unselect, 500);
  176. }
  177. else if (link == $("backButton"))
  178. history.back();
  179. else if (link.getAttribute("type") == "submit")
  180. {
  181. var form = findParent(link, "form");
  182. if (form.target == "_self")
  183. {
  184. form.submit();
  185. return; // return so we don't preventDefault
  186. }
  187. submitForm(form);
  188. }
  189. else if (link.getAttribute("type") == "cancel")
  190. cancelDialog(findParent(link, "form"));
  191. else if (link.target == "_replace")
  192. {
  193. link.setAttribute("selected", "progress");
  194. iui.showPageByHref(link.href, null, null, link, unselect);
  195. }
  196. else if (iui.isNativeUrl(link.href))
  197. {
  198. return;
  199. }
  200. else if (link.target == "_webapp")
  201. {
  202. location.href = link.href;
  203. }
  204. else if (!link.target)
  205. {
  206. link.setAttribute("selected", "progress");
  207. var bw = link.getAttribute("backwards");
  208. iui.showPageByHref(link.href, null, null, null, unselect, bw);
  209. }
  210. else
  211. return;
  212. event.preventDefault();
  213. }
  214. }, true);
  215. addEventListener("click", function(event)
  216. {
  217. var div = findParent(event.target, "div");
  218. if (div && hasClass(div, "toggle"))
  219. {
  220. div.setAttribute("toggled", div.getAttribute("toggled") != "true");
  221. event.preventDefault();
  222. }
  223. }, true);
  224. function getPageFromLoc()
  225. {
  226. var page;
  227. var result = location.hash.match(/#_([^\?_]+)/);
  228. if (result)
  229. page = result[1];
  230. if (page)
  231. page = $(page);
  232. return page;
  233. }
  234. function orientChangeHandler()
  235. {
  236. var orientation=window.orientation;
  237. switch(orientation)
  238. {
  239. case 0:
  240. setOrientation(portraitVal);
  241. break;
  242. case 90:
  243. case -90:
  244. setOrientation(landscapeVal);
  245. break;
  246. }
  247. }
  248. function checkOrientAndLocation()
  249. {
  250. if (!hasOrientationEvent)
  251. {
  252. if (window.innerWidth != currentWidth)
  253. {
  254. currentWidth = window.innerWidth;
  255. var orient = currentWidth == 320 ? portraitVal : landscapeVal;
  256. setOrientation(orient);
  257. }
  258. }
  259. if (location.hash != currentHash)
  260. {
  261. var pageId = location.hash.substr(hashPrefix.length);
  262. iui.showPageById(pageId);
  263. }
  264. }
  265. function setOrientation(orient)
  266. {
  267. document.body.setAttribute("orient", orient);
  268. setTimeout(scrollTo, 100, 0, 1);
  269. }
  270. function showDialog(page)
  271. {
  272. currentDialog = page;
  273. page.setAttribute("selected", "true");
  274. if (hasClass(page, "dialog") && !page.target)
  275. showForm(page);
  276. }
  277. function showForm(form)
  278. {
  279. form.onsubmit = function(event)
  280. {
  281. event.preventDefault();
  282. submitForm(form);
  283. };
  284. form.onclick = function(event)
  285. {
  286. if (event.target == form && hasClass(form, "dialog"))
  287. cancelDialog(form);
  288. };
  289. }
  290. function cancelDialog(form)
  291. {
  292. form.removeAttribute("selected");
  293. }
  294. function updatePage(page, fromPage)
  295. {
  296. if (!page.id)
  297. page.id = "__" + (++newPageCount) + "__";
  298. location.hash = currentHash = hashPrefix + page.id;
  299. pageHistory.push(page.id);
  300. var pageTitle = $("pageTitle");
  301. if (page.title)
  302. pageTitle.innerHTML = page.title;
  303. if (page.localName.toLowerCase() == "form" && !page.target)
  304. showForm(page);
  305. var backButton = $("backButton");
  306. if (backButton)
  307. {
  308. var prevPage = $(pageHistory[pageHistory.length-2]);
  309. if (prevPage && !page.getAttribute("hideBackButton"))
  310. {
  311. backButton.style.display = "inline";
  312. backButton.innerHTML = prevPage.title ? prevPage.title : "Back";
  313. }
  314. else
  315. backButton.style.display = "none";
  316. }
  317. var backButton = $("myBackButton");
  318. if (backButton)
  319. {
  320. var label = page.getAttribute("myBackLabel");
  321. if (label)
  322. {
  323. backButton.style.display = "inline";
  324. backButton.innerHTML = label;
  325. backButton.href = page.getAttribute("myBackHref");
  326. //backButton.target = page.getAttribute("myBackTarget");
  327. target = page.getAttribute("myBackTarget");
  328. if (target == null)
  329. backButton.target = '';
  330. else
  331. backButton.target = target;
  332. backButton.setAttribute("backwards", "true");
  333. }
  334. else
  335. backButton.style.display = "none";
  336. }
  337. }
  338. function slidePages(fromPage, toPage, backwards)
  339. {
  340. var axis = (backwards ? fromPage : toPage).getAttribute("axis");
  341. clearInterval(checkTimer);
  342. if (canDoSlideAnim() && axis != 'y')
  343. {
  344. slide2(fromPage, toPage, backwards, slideDone);
  345. }
  346. else
  347. {
  348. slide1(fromPage, toPage, backwards, axis, slideDone);
  349. }
  350. function slideDone()
  351. {
  352. if (!hasClass(toPage, "dialog"))
  353. fromPage.removeAttribute("selected");
  354. checkTimer = setInterval(checkOrientAndLocation, 300);
  355. setTimeout(updatePage, 0, toPage, fromPage);
  356. fromPage.removeEventListener('webkitTransitionEnd', slideDone, false);
  357. }
  358. }
  359. function canDoSlideAnim()
  360. {
  361. return (iui.animOn) && (typeof WebKitCSSMatrix == "object");
  362. }
  363. function slide1(fromPage, toPage, backwards, axis, cb)
  364. {
  365. if (axis == "y")
  366. (backwards ? fromPage : toPage).style.top = "100%";
  367. else
  368. toPage.style.left = "100%";
  369. scrollTo(0, 1);
  370. toPage.setAttribute("selected", "true");
  371. var percent = 100;
  372. slide();
  373. var timer = setInterval(slide, slideInterval);
  374. function slide()
  375. {
  376. percent -= slideSpeed;
  377. if (percent <= 0)
  378. {
  379. percent = 0;
  380. clearInterval(timer);
  381. cb();
  382. }
  383. if (axis == "y")
  384. {
  385. backwards
  386. ? fromPage.style.top = (100-percent) + "%"
  387. : toPage.style.top = percent + "%";
  388. }
  389. else
  390. {
  391. fromPage.style.left = (backwards ? (100-percent) : (percent-100)) + "%";
  392. toPage.style.left = (backwards ? -percent : percent) + "%";
  393. }
  394. }
  395. }
  396. function slide2(fromPage, toPage, backwards, cb)
  397. {
  398. toPage.style.webkitTransitionDuration = '0ms'; // Turn off transitions to set toPage start offset
  399. // fromStart is always 0% and toEnd is always 0%
  400. // iPhone won't take % width on toPage
  401. var toStart = 'translateX(' + (backwards ? '-' : '') + window.innerWidth + 'px)';
  402. var fromEnd = 'translateX(' + (backwards ? '100%' : '-100%') + ')';
  403. toPage.style.webkitTransform = toStart;
  404. toPage.setAttribute("selected", "true");
  405. toPage.style.webkitTransitionDuration = ''; // Turn transitions back on
  406. function startTrans()
  407. {
  408. fromPage.style.webkitTransform = fromEnd;
  409. toPage.style.webkitTransform = 'translateX(0%)'; //toEnd
  410. }
  411. fromPage.addEventListener('webkitTransitionEnd', cb, false);
  412. setTimeout(startTrans, 0);
  413. }
  414. function preloadImages()
  415. {
  416. var preloader = document.createElement("div");
  417. preloader.id = "preloader";
  418. document.body.appendChild(preloader);
  419. }
  420. function submitForm(form)
  421. {
  422. iui.showPageByHref(form.action || "POST", encodeForm(form), form.method);
  423. }
  424. function encodeForm(form)
  425. {
  426. function encode(inputs)
  427. {
  428. for (var i = 0; i < inputs.length; ++i)
  429. {
  430. if (inputs[i].name)
  431. args.push(inputs[i].name + "=" + escape(inputs[i].value));
  432. }
  433. }
  434. var args = [];
  435. encode(form.getElementsByTagName("input"));
  436. encode(form.getElementsByTagName("textarea"));
  437. encode(form.getElementsByTagName("select"));
  438. return args;
  439. }
  440. function findParent(node, localName)
  441. {
  442. while (node && (node.nodeType != 1 || node.localName.toLowerCase() != localName))
  443. node = node.parentNode;
  444. return node;
  445. }
  446. function hasClass(self, name)
  447. {
  448. var re = new RegExp("(^|\\s)"+name+"($|\\s)");
  449. return re.exec(self.getAttribute("class")) != null;
  450. }
  451. function replaceElementWithSource(replace, source)
  452. {
  453. var page = replace.parentNode;
  454. var parent = replace;
  455. while (page.parentNode != document.body)
  456. {
  457. page = page.parentNode;
  458. parent = parent.parentNode;
  459. }
  460. var frag = document.createElement(parent.localName);
  461. frag.innerHTML = source;
  462. page.removeChild(parent);
  463. while (frag.firstChild)
  464. page.appendChild(frag.firstChild);
  465. }
  466. function $(id) { return document.getElementById(id); }
  467. function ddd() { console.log.apply(console, arguments); }
  468. })();