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