]> git.wh0rd.org - tt-rss.git/blob - lib/iui/iui.js
6001b27bcc6210f66d4855938124dbcb30b18b2b
[tt-rss.git] / lib / iui / iui.js
1 /*
2 Copyright (c) 2007-9, iUI Project Members
3 See LICENSE.txt for licensing terms
4 */
5
6
7 (function() {
8
9 var slideSpeed = 20;
10 var slideInterval = 0;
11
12 var currentPage = null;
13 var currentDialog = null;
14 var currentWidth = 0;
15 var currentHash = location.hash;
16 var hashPrefix = "#_";
17 var pageHistory = [];
18 var newPageCount = 0;
19 var checkTimer;
20 var hasOrientationEvent = false;
21 var portraitVal = "portrait";
22 var landscapeVal = "landscape";
23
24 // *************************************************************************************************
25
26 window.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
166 addEventListener("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
188 addEventListener("unload", function(event)
189 {
190 return;
191 }, false);
192
193 addEventListener("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 }
206 else if (link == $("backButton")) {
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");
236 iui.showPageByHref(link.href, null, null, null, unselect);
237 }
238 else
239 return;
240
241 event.preventDefault();
242 }
243 }, true);
244
245 addEventListener("click", function(event)
246 {
247 var div = findParent(event.target, "div");
248 if (div && hasClass(div, "toggle"))
249 {
250 div.setAttribute("toggled", div.getAttribute("toggled") != "true");
251 event.preventDefault();
252 }
253 }, true);
254
255 function getPageFromLoc()
256 {
257 var page;
258 var result = location.hash.match(/#_([^\?_]+)/);
259 if (result)
260 page = result[1];
261 if (page)
262 page = $(page);
263 return page;
264 }
265
266 function orientChangeHandler()
267 {
268 var orientation=window.orientation;
269 switch(orientation)
270 {
271 case 0:
272 setOrientation(portraitVal);
273 break;
274
275 case 90:
276 case -90:
277 setOrientation(landscapeVal);
278 break;
279 }
280 }
281
282
283 function checkOrientAndLocation()
284 {
285 if (!hasOrientationEvent)
286 {
287 if (window.innerWidth != currentWidth)
288 {
289 currentWidth = window.innerWidth;
290 var orient = currentWidth == 320 ? portraitVal : landscapeVal;
291 setOrientation(orient);
292 }
293 }
294
295 if (location.hash != currentHash)
296 {
297 var pageId = location.hash.substr(hashPrefix.length);
298 iui.showPageById(pageId);
299 }
300 }
301
302 function setOrientation(orient)
303 {
304 document.body.setAttribute("orient", orient);
305 setTimeout(scrollTo, 100, 0, 1);
306 }
307
308 function showDialog(page)
309 {
310 currentDialog = page;
311 page.setAttribute("selected", "true");
312
313 if (hasClass(page, "dialog") && !page.target)
314 showForm(page);
315 }
316
317 function showForm(form)
318 {
319 form.onsubmit = function(event)
320 {
321 event.preventDefault();
322 submitForm(form);
323 };
324
325 form.onclick = function(event)
326 {
327 if (event.target == form && hasClass(form, "dialog"))
328 cancelDialog(form);
329 };
330 }
331
332 function cancelDialog(form)
333 {
334 form.removeAttribute("selected");
335 }
336
337 function updatePage(page, fromPage)
338 {
339 if (!page.id)
340 page.id = "__" + (++newPageCount) + "__";
341
342 location.hash = currentHash = hashPrefix + page.id;
343 pageHistory.push(page.id);
344
345 var pageTitle = $("pageTitle");
346 if (page.title)
347 pageTitle.innerHTML = page.title;
348
349 if (page.localName.toLowerCase() == "form" && !page.target)
350 showForm(page);
351
352 var backButton = $("backButton");
353 if (backButton)
354 {
355 var prevPage = $(pageHistory[pageHistory.length-2]);
356 if (prevPage && !page.getAttribute("hideBackButton"))
357 {
358 backButton.style.display = "inline";
359 backButton.innerHTML = prevPage.title ? prevPage.title : "Back";
360 }
361 else
362 backButton.style.display = "none";
363 }
364 }
365
366 function slidePages(fromPage, toPage, backwards)
367 {
368 var axis = (backwards ? fromPage : toPage).getAttribute("axis");
369
370 clearInterval(checkTimer);
371
372 if (canDoSlideAnim() && axis != 'y')
373 {
374 slide2(fromPage, toPage, backwards, slideDone);
375 }
376 else
377 {
378 slide1(fromPage, toPage, backwards, axis, slideDone);
379 }
380
381 function slideDone()
382 {
383 if (!hasClass(toPage, "dialog"))
384 fromPage.removeAttribute("selected");
385 checkTimer = setInterval(checkOrientAndLocation, 300);
386 setTimeout(updatePage, 0, toPage, fromPage);
387 fromPage.removeEventListener('webkitTransitionEnd', slideDone, false);
388 }
389 }
390
391 function canDoSlideAnim()
392 {
393 return (iui.animOn) && (typeof WebKitCSSMatrix == "object");
394 }
395
396 function slide1(fromPage, toPage, backwards, axis, cb)
397 {
398 if (axis == "y")
399 (backwards ? fromPage : toPage).style.top = "100%";
400 else
401 toPage.style.left = "100%";
402
403 scrollTo(0, 1);
404 toPage.setAttribute("selected", "true");
405 var percent = 100;
406 slide();
407 var timer = setInterval(slide, slideInterval);
408
409 function slide()
410 {
411 percent -= slideSpeed;
412 if (percent <= 0)
413 {
414 percent = 0;
415 clearInterval(timer);
416 cb();
417 }
418
419 if (axis == "y")
420 {
421 backwards
422 ? fromPage.style.top = (100-percent) + "%"
423 : toPage.style.top = percent + "%";
424 }
425 else
426 {
427 fromPage.style.left = (backwards ? (100-percent) : (percent-100)) + "%";
428 toPage.style.left = (backwards ? -percent : percent) + "%";
429 }
430 }
431 }
432
433
434 function slide2(fromPage, toPage, backwards, cb)
435 {
436 toPage.style.webkitTransitionDuration = '0ms'; // Turn off transitions to set toPage start offset
437 // fromStart is always 0% and toEnd is always 0%
438 // iPhone won't take % width on toPage
439 var toStart = 'translateX(' + (backwards ? '-' : '') + window.innerWidth + 'px)';
440 var fromEnd = 'translateX(' + (backwards ? '100%' : '-100%') + ')';
441 toPage.style.webkitTransform = toStart;
442 toPage.setAttribute("selected", "true");
443 toPage.style.webkitTransitionDuration = ''; // Turn transitions back on
444 function startTrans()
445 {
446 fromPage.style.webkitTransform = fromEnd;
447 toPage.style.webkitTransform = 'translateX(0%)'; //toEnd
448 }
449 fromPage.addEventListener('webkitTransitionEnd', cb, false);
450 setTimeout(startTrans, 0);
451 }
452
453 function preloadImages()
454 {
455 var preloader = document.createElement("div");
456 preloader.id = "preloader";
457 document.body.appendChild(preloader);
458 }
459
460 function submitForm(form)
461 {
462 iui.showPageByHref(form.action || "POST", encodeForm(form), form.method);
463 }
464
465 function encodeForm(form)
466 {
467 function encode(inputs)
468 {
469 for (var i = 0; i < inputs.length; ++i)
470 {
471 if (inputs[i].name)
472 args.push(inputs[i].name + "=" + escape(inputs[i].value));
473 }
474 }
475
476 var args = [];
477 encode(form.getElementsByTagName("input"));
478 encode(form.getElementsByTagName("textarea"));
479 encode(form.getElementsByTagName("select"));
480 return args;
481 }
482
483 function findParent(node, localName)
484 {
485 while (node && (node.nodeType != 1 || node.localName.toLowerCase() != localName))
486 node = node.parentNode;
487 return node;
488 }
489
490 function hasClass(self, name)
491 {
492 var re = new RegExp("(^|\\s)"+name+"($|\\s)");
493 return re.exec(self.getAttribute("class")) != null;
494 }
495
496 function replaceElementWithSource(replace, source)
497 {
498 var page = replace.parentNode;
499 var parent = replace;
500 while (page.parentNode != document.body)
501 {
502 page = page.parentNode;
503 parent = parent.parentNode;
504 }
505
506 var frag = document.createElement(parent.localName);
507 frag.innerHTML = source;
508
509 page.removeChild(parent);
510
511 while (frag.firstChild)
512 page.appendChild(frag.firstChild);
513 }
514
515 function $(id) { return document.getElementById(id); }
516 function ddd() { console.log.apply(console, arguments); }
517
518 })();