Upgrade to jquery.mobile-1.0rc1
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@
/labs/tiles/16
/labs/tiles/17
/labs/tiles/19
+/nbproject/private/
--- a/aws/busuiphp.sh
+++ b/aws/busuiphp.sh
@@ -1,16 +1,12 @@
cp /root/aws.php /tmp/
-mkdir /var/www/lib/staticmaplite/cache
+chmod 777 /var/cache/lighttpd/compress/
+
chcon -h system_u:object_r:httpd_sys_content_t /var/www
chcon -R -h root:object_r:httpd_sys_content_t /var/www/*
-
-chcon -R -t httpd_sys_content_rw_t /var/www/lib/staticmaplite/cache
-chmod -R 777 /var/www/lib/staticmaplite/cache
chcon -R -t httpd_sys_content_rw_t /var/www/labs/tiles
chmod -R 777 /var/www/labs/tiles
-chcon -R -t httpd_sys_content_rw_t /var/www/lib/openid-php/oid_store
-chmod -R 777 /var/www/lib/openid-php/oid_store
-
wget http://s3-ap-southeast-1.amazonaws.com/busresources/cbrfeed.zip \
-O /var/www/cbrfeed.zip
+
--- a/css/jquery.mobile-1.0b3.css
+++ /dev/null
@@ -1,1700 +1,1 @@
-/*!
- * jQuery Mobile v1.0b3
- * http://jquerymobile.com/
- *
- * Copyright 2010, jQuery Project
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- */
-/*
-* jQuery Mobile Framework
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
-*/
-
-/* A
------------------------------------------------------------------------------------------------------------*/
-
-.ui-bar-a {
- border: 1px solid #2A2A2A;
- background: #111111;
- color: #ffffff;
- font-weight: bold;
- text-shadow: 0 -1px 1px #000000;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#111)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #3c3c3c, #111); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #3c3c3c, #111); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #3c3c3c, #111); /* IE10 */
- background-image: -o-linear-gradient(top, #3c3c3c, #111); /* Opera 11.10+ */
- background-image: linear-gradient(top, #3c3c3c, #111);
-}
-.ui-bar-a,
-.ui-bar-a input,
-.ui-bar-a select,
-.ui-bar-a textarea,
-.ui-bar-a button {
- font-family: Helvetica, Arial, sans-serif;
-}
-.ui-bar-a .ui-link-inherit {
- color: #fff;
-}
-.ui-bar-a .ui-link {
- color: #7cc4e7;
- font-weight: bold;
-}
-.ui-body-a {
- border: 1px solid #2A2A2A;
- background: #222222;
- color: #fff;
- text-shadow: 0 1px 0 #000;
- font-weight: normal;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#666), to(#222)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #666, #222); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #666, #222); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #666, #222); /* IE10 */
- background-image: -o-linear-gradient(top, #666, #222); /* Opera 11.10+ */
- background-image: linear-gradient(top, #666, #222);
-}
-.ui-body-a,
-.ui-body-a input,
-.ui-body-a select,
-.ui-body-a textarea,
-.ui-body-a button {
- font-family: Helvetica, Arial, sans-serif;
-}
-.ui-body-a .ui-link-inherit {
- color: #fff;
-}
-.ui-body-a .ui-link {
- color: #2489CE;
- font-weight: bold;
-}
-.ui-br {
- border-bottom: rgb(130,130,130);
- border-bottom: rgba(130,130,130,.3);
- border-bottom-width: 1px;
- border-bottom-style: solid;
-}
-.ui-btn-up-a {
- border: 1px solid #222;
- background: #333333;
- font-weight: bold;
- color: #fff;
- text-shadow: 0 -1px 1px #000;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#555), to(#333)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #555, #333); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #555, #333); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #555, #333); /* IE10 */
- background-image: -o-linear-gradient(top, #555, #333); /* Opera 11.10+ */
- background-image: linear-gradient(top, #555, #333);
-}
-.ui-btn-up-a a.ui-link-inherit {
- color: #fff;
-}
-.ui-btn-hover-a {
- border: 1px solid #000;
- background: #444444;
- font-weight: bold;
- color: #fff;
- text-shadow: 0 -1px 1px #000;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#666), to(#444)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #666, #444); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #666, #444); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #666, #444); /* IE10 */
- background-image: -o-linear-gradient(top, #666, #444); /* Opera 11.10+ */
- background-image: linear-gradient(top, #666, #444);
-}
-.ui-btn-hover-a a.ui-link-inherit {
- color: #fff;
-}
-.ui-btn-down-a {
- border: 1px solid #000;
- background: #3d3d3d;
- font-weight: bold;
- color: #fff;
- text-shadow: 0 -1px 1px #000;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#333), to(#5a5a5a)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #333, #5a5a5a); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #333, #5a5a5a); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #333, #5a5a5a); /* IE10 */
- background-image: -o-linear-gradient(top, #333, #5a5a5a); /* Opera 11.10+ */
- background-image: linear-gradient(top, #333, #5a5a5a);
-}
-.ui-btn-down-a a.ui-link-inherit {
- color: #fff;
-}
-.ui-btn-up-a,
-.ui-btn-hover-a,
-.ui-btn-down-a {
- font-family: Helvetica, Arial, sans-serif;
- text-decoration: none;
-}
-
-
-/* B
------------------------------------------------------------------------------------------------------------*/
-
-.ui-bar-b {
- border: 1px solid #456f9a;
- background: #5e87b0;
- color: #fff;
- font-weight: bold;
- text-shadow: 0 -1px 1px #254f7a;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#81a8ce), to(#5e87b0)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #81a8ce, #5e87b0); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #81a8ce, #5e87b0); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #81a8ce, #5e87b0); /* IE10 */
- background-image: -o-linear-gradient(top, #81a8ce, #5e87b0); /* Opera 11.10+ */
- background-image: linear-gradient(top, #81a8ce, #5e87b0);
-}
-.ui-bar-b,
-.ui-bar-b input,
-.ui-bar-b select,
-.ui-bar-b textarea,
-.ui-bar-b button {
- font-family: Helvetica, Arial, sans-serif;
-}
-.ui-bar-b .ui-link-inherit {
- color: #fff;
-}
-.ui-bar-b .ui-link {
- color: #7cc4e7;
- font-weight: bold;
-}
-
-.ui-body-b {
- border: 1px solid #C6C6C6;
- background: #cccccc;
- color: #333333;
- text-shadow: 0 1px 0 #fff;
- font-weight: normal;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#e6e6e6), to(#ccc)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #e6e6e6, #ccc); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #e6e6e6, #ccc); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #e6e6e6, #ccc); /* IE10 */
- background-image: -o-linear-gradient(top, #e6e6e6, #ccc); /* Opera 11.10+ */
- background-image: linear-gradient(top, #e6e6e6, #ccc);
-}
-.ui-body-b,
-.ui-body-b input,
-.ui-body-b select,
-.ui-body-b textarea,
-.ui-body-b button {
- font-family: Helvetica, Arial, sans-serif;
-}
-.ui-body-b .ui-link-inherit {
- color: #333333;
-}
-.ui-body-b .ui-link {
- color: #2489CE;
- font-weight: bold;
-}
-.ui-btn-up-b {
- border: 1px solid #145072;
- background: #2567ab;
- font-weight: bold;
- color: #fff;
- text-shadow: 0 -1px 1px #145072;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#5f9cc5), to(#396b9e)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #5f9cc5, #396b9e); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #5f9cc5, #396b9e); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #5f9cc5, #396b9e); /* IE10 */
- background-image: -o-linear-gradient(top, #5f9cc5, #396b9e); /* Opera 11.10+ */
- background-image: linear-gradient(top, #5f9cc5, #396b9e);
-}
-.ui-btn-up-b a.ui-link-inherit {
- color: #fff;
-}
-.ui-btn-hover-b {
- border: 1px solid #00516e;
- background: #4b88b6;
- font-weight: bold;
- color: #fff;
- text-shadow: 0 -1px 1px #014D68;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#72b0d4), to(#4b88b6)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #72b0d4, #4b88b6); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #72b0d4, #4b88b6); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #72b0d4, #4b88b6); /* IE10 */
- background-image: -o-linear-gradient(top, #72b0d4, #4b88b6); /* Opera 11.10+ */
- background-image: linear-gradient(top, #72b0d4, #4b88b6);
-}
-.ui-btn-hover-b a.ui-link-inherit {
- color: #fff;
-}
-.ui-btn-down-b {
- border: 1px solid #225377;
- background: #4e89c5;
- font-weight: bold;
- color: #fff;
- text-shadow: 0 -1px 1px #225377;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#396b9e), to(#4e89c5)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #396b9e, #4e89c5); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #396b9e, #4e89c5); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #396b9e, #4e89c5); /* IE10 */
- background-image: -o-linear-gradient(top, #396b9e, #4e89c5); /* Opera 11.10+ */
- background-image: linear-gradient(top, #396b9e, #4e89c5);
-}
-.ui-btn-down-b a.ui-link-inherit {
- color: #fff;
-}
-.ui-btn-up-b,
-.ui-btn-hover-b,
-.ui-btn-down-b {
- font-family: Helvetica, Arial, sans-serif;
- text-decoration: none;
-}
-
-
-/* C
------------------------------------------------------------------------------------------------------------*/
-
-.ui-bar-c {
- border: 1px solid #B3B3B3;
- background: #e9eaeb;
- color: #3E3E3E;
- font-weight: bold;
- text-shadow: 0 1px 1px #fff;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#f0f0f0), to(#e9eaeb)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #f0f0f0, #e9eaeb); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #f0f0f0, #e9eaeb); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #f0f0f0, #e9eaeb); /* IE10 */
- background-image: -o-linear-gradient(top, #f0f0f0, #e9eaeb); /* Opera 11.10+ */
- background-image: linear-gradient(top, #f0f0f0, #e9eaeb);
-}
-.ui-bar-c,
-.ui-bar-c input,
-.ui-bar-c select,
-.ui-bar-c textarea,
-.ui-bar-c button {
- font-family: Helvetica, Arial, sans-serif;
-}
-.ui-body-c {
- border: 1px solid #B3B3B3;
- color: #333333;
- text-shadow: 0 1px 0 #fff;
- background: #f0f0f0;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#ddd)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #eee, #ddd); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #eee, #ddd); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #eee, #ddd); /* IE10 */
- background-image: -o-linear-gradient(top, #eee, #ddd); /* Opera 11.10+ */
- background-image: linear-gradient(top, #eee, #ddd);
-}
-.ui-body-c,
-.ui-body-c input,
-.ui-body-c select,
-.ui-body-c textarea,
-.ui-body-c button {
- font-family: Helvetica, Arial, sans-serif;
-}
-.ui-body-c .ui-link-inherit {
- color: #333333;
-}
-.ui-body-c .ui-link {
- color: #2489CE;
- font-weight: bold;
-}
-
-.ui-btn-up-c {
- border: 1px solid #ccc;
- background: #eee;
- font-weight: bold;
- color: #444;
- text-shadow: 0 1px 1px #f6f6f6;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#fdfdfd), to(#eee)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #fdfdfd, #eee); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #fdfdfd, #eee); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #fdfdfd, #eee); /* IE10 */
- background-image: -o-linear-gradient(top, #fdfdfd, #eee); /* Opera 11.10+ */
- background-image: linear-gradient(top, #fdfdfd, #eee);
-}
-.ui-btn-up-c a.ui-link-inherit {
- color: #2F3E46;
-}
-
-.ui-btn-hover-c {
- border: 1px solid #bbb;
- background: #dadada;
- font-weight: bold;
- color: #101010;
- text-shadow: 0 1px 1px #fff;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#ededed), to(#dadada)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #ededed, #dadada); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #ededed, #dadada); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #ededed, #dadada); /* IE10 */
- background-image: -o-linear-gradient(top, #ededed, #dadada); /* Opera 11.10+ */
- background-image: linear-gradient(top, #ededed, #dadada);
-}
-.ui-btn-hover-c a.ui-link-inherit {
- color: #2F3E46;
-}
-.ui-btn-down-c {
- border: 1px solid #808080;
- background: #fdfdfd;
- font-weight: bold;
- color: #111111;
- text-shadow: 0 1px 1px #ffffff;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#fdfdfd)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #eee, #fdfdfd); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #eee, #fdfdfd); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #eee, #fdfdfd); /* IE10 */
- background-image: -o-linear-gradient(top, #eee, #fdfdfd); /* Opera 11.10+ */
- background-image: linear-gradient(top, #eee, #fdfdfd);
-}
-.ui-btn-down-c a.ui-link-inherit {
- color: #2F3E46;
-}
-.ui-btn-up-c,
-.ui-btn-hover-c,
-.ui-btn-down-c {
- font-family: Helvetica, Arial, sans-serif;
- text-decoration: none;
-}
-
-
-/* D
------------------------------------------------------------------------------------------------------------*/
-
-.ui-bar-d {
- border: 1px solid #ccc;
- background: #bbb;
- color: #333;
- text-shadow: 0 1px 0 #eee;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#ddd), to(#bbb)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #ddd, #bbb); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #ddd, #bbb); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #ddd, #bbb); /* IE10 */
- background-image: -o-linear-gradient(top, #ddd, #bbb); /* Opera 11.10+ */
- background-image: linear-gradient(top, #ddd, #bbb);
-}
-.ui-bar-d,
-.ui-bar-d input,
-.ui-bar-d select,
-.ui-bar-d textarea,
-.ui-bar-d button {
- font-family: Helvetica, Arial, sans-serif;
-}
-.ui-bar-d .ui-link-inherit {
- color: #333;
-}
-.ui-bar-d .ui-link {
- color: #2489CE;
- font-weight: bold;
-}
-.ui-body-d {
- border: 1px solid #ccc;
- color: #333333;
- text-shadow: 0 1px 0 #fff;
- background: #ffffff;
-}
-.ui-body-d,
-.ui-body-d input,
-.ui-body-d select,
-.ui-body-d textarea,
-.ui-body-d button {
- font-family: Helvetica, Arial, sans-serif;
-}
-.ui-body-d .ui-link-inherit {
- color: #333333;
-}
-.ui-body-d .ui-link {
- color: #2489CE;
- font-weight: bold;
-}
-.ui-btn-up-d {
- border: 1px solid #ccc;
- background: #fff;
- font-weight: bold;
- color: #444;
- text-shadow: 0 1px 1px #fff;
-}
-.ui-btn-up-d a.ui-link-inherit {
- color: #333;
-}
-.ui-btn-hover-d {
- border: 1px solid #aaa;
- background: #eeeeee;
- font-weight: bold;
- color: #222;
- cursor: pointer;
- text-shadow: 0 1px 1px #fff;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#fdfdfd), to(#eee)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #fdfdfd, #eee); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #fdfdfd, #eee); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #fdfdfd, #eee); /* IE10 */
- background-image: -o-linear-gradient(top, #fdfdfd, #eee); /* Opera 11.10+ */
- background-image: linear-gradient(top, #fdfdfd, #eee);
-}
-.ui-btn-hover-d a.ui-link-inherit {
- color: #222;
-}
-.ui-btn-down-d {
- border: 1px solid #aaaaaa;
- background: #ffffff;
- font-weight: bold;
- color: #111;
- text-shadow: 0 1px 1px #ffffff;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#fff)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #eee, #fff); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #eee, #fff); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #eee, #fff); /* IE10 */
- background-image: -o-linear-gradient(top, #eee, #fff); /* Opera 11.10+ */
- background-image: linear-gradient(top, #eee, #fff);
-}
-.ui-btn-down-d a.ui-link-inherit {
- color: #111;
-}
-.ui-btn-up-d,
-.ui-btn-hover-d,
-.ui-btn-down-d {
- font-family: Helvetica, Arial, sans-serif;
- text-decoration: none;
-}
-
-
-/* E
------------------------------------------------------------------------------------------------------------*/
-
-.ui-bar-e {
- border: 1px solid #F7C942;
- background: #fadb4e;
- color: #333;
- text-shadow: 0 1px 0 #fff;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#fceda7), to(#fadb4e)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #fceda7, #fadb4e); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #fceda7, #fadb4e); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #fceda7, #fadb4e); /* IE10 */
- background-image: -o-linear-gradient(top, #fceda7, #fadb4e); /* Opera 11.10+ */
- background-image: linear-gradient(top, #fceda7, #fadb4e);
-}
-.ui-bar-e,
-.ui-bar-e input,
-.ui-bar-e select,
-.ui-bar-e textarea,
-.ui-bar-e button {
- font-family: Helvetica, Arial, sans-serif;
-}
-.ui-bar-e .ui-link-inherit {
- color: #333;
-}
-.ui-bar-e .ui-link {
- color: #2489CE;
- font-weight: bold;
-}
-.ui-body-e {
- border: 1px solid #F7C942;
- color: #333333;
- text-shadow: 0 1px 0 #fff;
- background: #faeb9e;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#faeb9e)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #fff, #faeb9e); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #fff, #faeb9e); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #fff, #faeb9e); /* IE10 */
- background-image: -o-linear-gradient(top, #fff, #faeb9e); /* Opera 11.10+ */
- background-image: linear-gradient(top, #fff, #faeb9e);
-}
-.ui-body-e,
-.ui-body-e input,
-.ui-body-e select,
-.ui-body-e textarea,
-.ui-body-e button {
- font-family: Helvetica, Arial, sans-serif;
-}
-.ui-body-e .ui-link-inherit {
- color: #333333;
-}
-.ui-body-e .ui-link {
- color: #2489CE;
- font-weight: bold;
-}
-.ui-btn-up-e {
- border: 1px solid #F7C942;
- background: #fadb4e;
- font-weight: bold;
- color: #333;
- text-shadow: 0 1px 0 #fff;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#fceda7), to(#fadb4e)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #fceda7, #fadb4e); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #fceda7, #fadb4e); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #fceda7, #fadb4e); /* IE10 */
- background-image: -o-linear-gradient(top, #fceda7, #fadb4e); /* Opera 11.10+ */
- background-image: linear-gradient(top, #fceda7, #fadb4e);
-}
-.ui-btn-up-e a.ui-link-inherit {
- color: #333;
-}
-.ui-btn-hover-e {
- border: 1px solid #e79952;
- background: #fbe26f;
- font-weight: bold;
- color: #111;
- text-shadow: 0 1px 1px #fff;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf0b5), to(#fbe26f)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #fcf0b5, #fbe26f); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #fcf0b5, #fbe26f); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #fcf0b5, #fbe26f); /* IE10 */
- background-image: -o-linear-gradient(top, #fcf0b5, #fbe26f); /* Opera 11.10+ */
- background-image: linear-gradient(top, #fcf0b5, #fbe26f);
-}
-
-.ui-btn-hover-e a.ui-link-inherit {
- color: #333;
-}
-.ui-btn-down-e {
- border: 1px solid #F7C942;
- background: #fceda7;
- font-weight: bold;
- color: #111;
- text-shadow: 0 1px 1px #ffffff;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#fadb4e), to(#fceda7)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #fadb4e, #fceda7); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #fadb4e, #fceda7); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #fadb4e, #fceda7); /* IE10 */
- background-image: -o-linear-gradient(top, #fadb4e, #fceda7); /* Opera 11.10+ */
- background-image: linear-gradient(top, #fadb4e, #fceda7);
-}
-.ui-btn-down-e a.ui-link-inherit {
- color: #333;
-}
-.ui-btn-up-e,
-.ui-btn-hover-e,
-.ui-btn-down-e {
- font-family: Helvetica, Arial, sans-serif;
- text-decoration: none;
-}
-
-
-/* links within "buttons"
------------------------------------------------------------------------------------------------------------*/
-
-a.ui-link-inherit {
- text-decoration: none !important;
-}
-
-
-/* Active class used as the "on" state across all themes
------------------------------------------------------------------------------------------------------------*/
-
-.ui-btn-active {
- border: 1px solid #155678;
- background: #4596ce;
- font-weight: bold;
- color: #fff;
- cursor: pointer;
- text-shadow: 0 -1px 1px #145072;
- text-decoration: none;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#85bae4), to(#5393c5)); /* Saf4+, Chrome */
- background-image: -webkit-linear-gradient(top, #85bae4, #5393c5); /* Chrome 10+, Saf5.1+ */
- background-image: -moz-linear-gradient(top, #85bae4, #5393c5); /* FF3.6 */
- background-image: -ms-linear-gradient(top, #85bae4, #5393c5); /* IE10 */
- background-image: -o-linear-gradient(top, #85bae4, #5393c5); /* Opera 11.10+ */
- background-image: linear-gradient(top, #85bae4, #5393c5);
- outline: none;
-}
-.ui-btn-active a.ui-link-inherit {
- color: #fff;
-}
-
-
-/* button inner top highlight
------------------------------------------------------------------------------------------------------------*/
-
-.ui-btn-inner {
- border-top: 1px solid #fff;
- border-color: rgba(255,255,255,.3);
-}
-
-
-/* corner rounding classes
------------------------------------------------------------------------------------------------------------*/
-
-.ui-corner-tl {
- -moz-border-radius-topleft: .6em;
- -webkit-border-top-left-radius: .6em;
- border-top-left-radius: .6em;
-}
-.ui-corner-tr {
- -moz-border-radius-topright: .6em;
- -webkit-border-top-right-radius: .6em;
- border-top-right-radius: .6em;
-}
-.ui-corner-bl {
- -moz-border-radius-bottomleft: .6em;
- -webkit-border-bottom-left-radius: .6em;
- border-bottom-left-radius: .6em;
-}
-.ui-corner-br {
- -moz-border-radius-bottomright: .6em;
- -webkit-border-bottom-right-radius: .6em;
- border-bottom-right-radius: .6em;
-}
-.ui-corner-top {
- -moz-border-radius-topleft: .6em;
- -webkit-border-top-left-radius: .6em;
- border-top-left-radius: .6em;
- -moz-border-radius-topright: .6em;
- -webkit-border-top-right-radius: .6em;
- border-top-right-radius: .6em;
-}
-.ui-corner-bottom {
- -moz-border-radius-bottomleft: .6em;
- -webkit-border-bottom-left-radius: .6em;
- border-bottom-left-radius: .6em;
- -moz-border-radius-bottomright: .6em;
- -webkit-border-bottom-right-radius: .6em;
- border-bottom-right-radius: .6em;
- }
-.ui-corner-right {
- -moz-border-radius-topright: .6em;
- -webkit-border-top-right-radius: .6em;
- border-top-right-radius: .6em;
- -moz-border-radius-bottomright: .6em;
- -webkit-border-bottom-right-radius: .6em;
- border-bottom-right-radius: .6em;
-}
-.ui-corner-left {
- -moz-border-radius-topleft: .6em;
- -webkit-border-top-left-radius: .6em;
- border-top-left-radius: .6em;
- -moz-border-radius-bottomleft: .6em;
- -webkit-border-bottom-left-radius: .6em;
- border-bottom-left-radius: .6em;
-}
-.ui-corner-all {
- -moz-border-radius: .6em;
- -webkit-border-radius: .6em;
- border-radius: .6em;
-}
-.ui-corner-none {
- -moz-border-radius: 0;
- -webkit-border-radius: 0;
- border-radius: 0;
-}
-
-/* Interaction cues
------------------------------------------------------------------------------------------------------------*/
-.ui-disabled {
- opacity: .3;
-}
-.ui-disabled,
-.ui-disabled a {
- cursor: default;
-}
-
-/* Icons
------------------------------------------------------------------------------------------------------------*/
-
-.ui-icon {
- background: #666;
- background: rgba(0,0,0,.4);
- background-image: url(images/icons-18-white.png);
- background-repeat: no-repeat;
- -moz-border-radius: 9px;
- -webkit-border-radius: 9px;
- border-radius: 9px;
-}
-
-
-/* Alt icon color
------------------------------------------------------------------------------------------------------------*/
-
-.ui-icon-alt {
- background: #fff;
- background: rgba(255,255,255,.3);
- background-image: url(images/icons-18-black.png);
- background-repeat: no-repeat;
-}
-
-/* HD/"retina" sprite
------------------------------------------------------------------------------------------------------------*/
-
-@media only screen and (-webkit-min-device-pixel-ratio: 1.5),
- only screen and (min--moz-device-pixel-ratio: 1.5),
- only screen and (min-resolution: 240dpi) {
-
- .ui-icon-plus, .ui-icon-minus, .ui-icon-delete, .ui-icon-arrow-r,
- .ui-icon-arrow-l, .ui-icon-arrow-u, .ui-icon-arrow-d, .ui-icon-check,
- .ui-icon-gear, .ui-icon-refresh, .ui-icon-forward, .ui-icon-back,
- .ui-icon-grid, .ui-icon-star, .ui-icon-alert, .ui-icon-info, .ui-icon-home, .ui-icon-search,
- .ui-icon-checkbox-off, .ui-icon-checkbox-on, .ui-icon-radio-off, .ui-icon-radio-on {
- background-image: url(images/icons-36-white.png);
- -moz-background-size: 776px 18px;
- -o-background-size: 776px 18px;
- -webkit-background-size: 776px 18px;
- background-size: 776px 18px;
- }
- .ui-icon-alt {
- background-image: url(images/icons-36-black.png);
- }
-}
-
-/* plus minus */
-.ui-icon-plus {
- background-position: -0 50%;
-}
-.ui-icon-minus {
- background-position: -36px 50%;
-}
-
-/* delete/close */
-.ui-icon-delete {
- background-position: -72px 50%;
-}
-
-/* arrows */
-.ui-icon-arrow-r {
- background-position: -108px 50%;
-}
-.ui-icon-arrow-l {
- background-position: -144px 50%;
-}
-.ui-icon-arrow-u {
- background-position: -180px 50%;
-}
-.ui-icon-arrow-d {
- background-position: -216px 50%;
-}
-
-/* misc */
-.ui-icon-check {
- background-position: -252px 50%;
-}
-.ui-icon-gear {
- background-position: -288px 50%;
-}
-.ui-icon-refresh {
- background-position: -324px 50%;
-}
-.ui-icon-forward {
- background-position: -360px 50%;
-}
-.ui-icon-back {
- background-position: -396px 50%;
-}
-.ui-icon-grid {
- background-position: -432px 50%;
-}
-.ui-icon-star {
- background-position: -468px 50%;
-}
-.ui-icon-alert {
- background-position: -504px 50%;
-}
-.ui-icon-info {
- background-position: -540px 50%;
-}
-.ui-icon-home {
- background-position: -576px 50%;
-}
-.ui-icon-search {
- background-position: -612px 50%;
-}
-.ui-icon-checkbox-off {
- background-position: -684px 50%;
-}
-.ui-icon-checkbox-on {
- background-position: -648px 50%;
-}
-.ui-icon-radio-off {
- background-position: -756px 50%;
-}
-.ui-icon-radio-on {
- background-position: -720px 50%;
-}
-
-
-/* checks,radios */
-.ui-checkbox .ui-icon {
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
-}
-.ui-icon-checkbox-off,
-.ui-icon-radio-off {
- background-color: transparent;
-}
-.ui-icon-checkbox-on,
-.ui-checkbox-on .ui-icon,
-.ui-radio-on .ui-icon {
- background-color: #4596ce; /* NOTE: this hex should match the active state color. It's repeated here for cascade */
-}
-.ui-icon-searchfield {
- background-image: url(images/icon-search-black.png);
- background-size: 16px 16px;
-}
-
-/* loading icon */
-.ui-icon-loading {
- background-image: url(images/ajax-loader.png);
- width: 40px;
- height: 40px;
- -moz-border-radius: 20px;
- -webkit-border-radius: 20px;
- border-radius: 20px;
- background-size: 35px 35px;
-}
-
-
-/* Button corner classes
------------------------------------------------------------------------------------------------------------*/
-
-.ui-btn-corner-tl {
- -moz-border-radius-topleft: 1em;
- -webkit-border-top-left-radius: 1em;
- border-top-left-radius: 1em;
-}
-.ui-btn-corner-tr {
- -moz-border-radius-topright: 1em;
- -webkit-border-top-right-radius: 1em;
- border-top-right-radius: 1em;
-}
-.ui-btn-corner-bl {
- -moz-border-radius-bottomleft: 1em;
- -webkit-border-bottom-left-radius: 1em;
- border-bottom-left-radius: 1em;
-}
-.ui-btn-corner-br {
- -moz-border-radius-bottomright: 1em;
- -webkit-border-bottom-right-radius: 1em;
- border-bottom-right-radius: 1em;
-}
-.ui-btn-corner-top {
- -moz-border-radius-topleft: 1em;
- -webkit-border-top-left-radius: 1em;
- border-top-left-radius: 1em;
- -moz-border-radius-topright: 1em;
- -webkit-border-top-right-radius: 1em;
- border-top-right-radius: 1em;
-}
-.ui-btn-corner-bottom {
- -moz-border-radius-bottomleft: 1em;
- -webkit-border-bottom-left-radius: 1em;
- border-bottom-left-radius: 1em;
- -moz-border-radius-bottomright: 1em;
- -webkit-border-bottom-right-radius: 1em;
- border-bottom-right-radius: 1em;
-}
-.ui-btn-corner-right {
- -moz-border-radius-topright: 1em;
- -webkit-border-top-right-radius: 1em;
- border-top-right-radius: 1em;
- -moz-border-radius-bottomright: 1em;
- -webkit-border-bottom-right-radius: 1em;
- border-bottom-right-radius: 1em;
-}
-.ui-btn-corner-left {
- -moz-border-radius-topleft: 1em;
- -webkit-border-top-left-radius: 1em;
- border-top-left-radius: 1em;
- -moz-border-radius-bottomleft: 1em;
- -webkit-border-bottom-left-radius: 1em;
- border-bottom-left-radius: 1em;
-}
-.ui-btn-corner-all {
- -moz-border-radius: 1em;
- -webkit-border-radius: 1em;
- border-radius: 1em;
-}
-
-/* radius clip workaround for cleaning up corner trapping */
-.ui-corner-tl,
-.ui-corner-tr,
-.ui-corner-bl,
-.ui-corner-br,
-.ui-corner-top,
-.ui-corner-bottom,
-.ui-corner-right,
-.ui-corner-left,
-.ui-corner-all,
-.ui-btn-corner-tl,
-.ui-btn-corner-tr,
-.ui-btn-corner-bl,
-.ui-btn-corner-br,
-.ui-btn-corner-top,
-.ui-btn-corner-bottom,
-.ui-btn-corner-right,
-.ui-btn-corner-left,
-.ui-btn-corner-all {
- -webkit-background-clip: padding-box;
- -moz-background-clip: padding;
- background-clip: padding-box;
-}
-
-/* Overlay / modal
------------------------------------------------------------------------------------------------------------*/
-
-.ui-overlay {
- background: #666;
- opacity: .5;
- filter: Alpha(Opacity=50);
- position: absolute;
- width: 100%;
- height: 100%;
-}
-.ui-overlay-shadow {
- -moz-box-shadow: 0px 0px 12px rgba(0,0,0,.6);
- -webkit-box-shadow: 0px 0px 12px rgba(0,0,0,.6);
- box-shadow: 0px 0px 12px rgba(0,0,0,.6);
-}
-.ui-shadow {
- -moz-box-shadow: 0px 1px 4px rgba(0,0,0,.3);
- -webkit-box-shadow: 0px 1px 4px rgba(0,0,0,.3);
- box-shadow: 0px 1px 4px rgba(0,0,0,.3);
-}
-.ui-bar-a .ui-shadow,
-.ui-bar-b .ui-shadow ,
-.ui-bar-c .ui-shadow {
- -moz-box-shadow: 0px 1px 0 rgba(255,255,255,.3);
- -webkit-box-shadow: 0px 1px 0 rgba(255,255,255,.3);
- box-shadow: 0px 1px 0 rgba(255,255,255,.3);
-}
-.ui-shadow-inset {
- -moz-box-shadow: inset 0px 1px 4px rgba(0,0,0,.2);
- -webkit-box-shadow: inset 0px 1px 4px rgba(0,0,0,.2);
- box-shadow: inset 0px 1px 4px rgba(0,0,0,.2);
-}
-.ui-icon-shadow {
- -moz-box-shadow: 0px 1px 0 rgba(255,255,255,.4);
- -webkit-box-shadow: 0px 1px 0 rgba(255,255,255,.4);
- box-shadow: 0px 1px 0 rgba(255,255,255,.4);
-}
-
-
-/* Focus state - set here for specificity
------------------------------------------------------------------------------------------------------------*/
-
-.ui-focus {
- -moz-box-shadow: 0px 0px 12px #387bbe;
- -webkit-box-shadow: 0px 0px 12px #387bbe;
- box-shadow: 0px 0px 12px #387bbe;
-}
-
-/* unset box shadow in browsers that don't do it right
------------------------------------------------------------------------------------------------------------*/
-
-.ui-mobile-nosupport-boxshadow * {
- -moz-box-shadow: none !important;
- -webkit-box-shadow: none !important;
- box-shadow: none !important;
-}
-
-/* ...and bring back focus */
-.ui-mobile-nosupport-boxshadow .ui-focus {
- outline-width: 2px;
-}/*
-* jQuery Mobile Framework
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
-*/
-
-/* some unsets - more probably needed */
-.ui-mobile, .ui-mobile body { height: 100%; }
-.ui-mobile fieldset, .ui-page { padding: 0; margin: 0; }
-.ui-mobile a img, .ui-mobile fieldset { border: 0; }
-
-/* responsive page widths */
-.ui-mobile-viewport { margin: 0; overflow-x: hidden; -webkit-text-size-adjust: none; -ms-text-size-adjust:none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); }
-
-/* "page" containers - full-screen views, one should always be in view post-pageload */
-.ui-mobile [data-role=page], .ui-mobile [data-role=dialog], .ui-page { top: 0; left: 0; width: 100%; min-height: 100%; position: absolute; display: none; border: 0; }
-.ui-mobile .ui-page-active { display: block; overflow: visible; }
-
-/*orientations from js are available */
-.portrait,
-.portrait .ui-page { min-height: 420px; }
-.landscape,
-.landscape .ui-page { min-height: 300px; }
-
-/* native overflow scrolling */
-.ui-page.ui-mobile-touch-overflow,
-.ui-mobile-touch-overflow.ui-native-fixed .ui-content {
- overflow: auto;
- height: 100%;
- -webkit-overflow-scrolling: touch;
- -moz-overflow-scrolling: touch;
- -o-overflow-scrolling: touch;
- -ms-overflow-scrolling: touch;
- overflow-scrolling: touch;
-}
-.ui-page.ui-mobile-pre-transition {
- display: block;
-}
-
-/* loading screen */
-.ui-loading .ui-mobile-viewport { overflow: hidden !important; }
-.ui-loading .ui-loader { display: block; }
-.ui-loading .ui-page { overflow: hidden; }
-.ui-loader { display: none; position: absolute; opacity: .85; z-index: 100; left: 50%; width: 200px; margin-left: -130px; margin-top: -35px; padding: 10px 30px; }
-.ui-loader h1 { font-size: 15px; text-align: center; }
-.ui-loader .ui-icon { position: static; display: block; opacity: .9; margin: 0 auto; width: 35px; height: 35px; background-color: transparent; }
-
-/*fouc*/
-.ui-mobile-rendering > * { visibility: hidden; }
-
-/*headers, content panels*/
-.ui-bar, .ui-body { position: relative; padding: .4em 15px; overflow: hidden; display: block; clear:both; }
-.ui-bar { font-size: 16px; margin: 0; }
-.ui-bar h1, .ui-bar h2, .ui-bar h3, .ui-bar h4, .ui-bar h5, .ui-bar h6 { margin: 0; padding: 0; font-size: 16px; display: inline-block; }
-
-.ui-header, .ui-footer { display: block; }
-.ui-page .ui-header, .ui-page .ui-footer { position: relative; }
-.ui-header .ui-btn-left { position: absolute; left: 10px; top: .4em; }
-.ui-header .ui-btn-right { position: absolute; right: 10px; top: .4em; }
-.ui-header .ui-title, .ui-footer .ui-title { min-height: 1.1em; text-align: center; font-size: 16px; display: block; margin: .6em 90px .8em; padding: 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; outline: 0 !important; }
-
-/*content area*/
-.ui-content { border-width: 0; overflow: visible; overflow-x: hidden; padding: 15px; }
-.ui-page-fullscreen .ui-content { padding:0; }
-
-/* native fixed headers and footers */
-.ui-mobile-touch-overflow.ui-page.ui-native-fixed,
-.ui-mobile-touch-overflow.ui-page.ui-native-fullscreen {
- overflow: visible;
-}
-.ui-mobile-touch-overflow.ui-native-fixed .ui-header,
-.ui-mobile-touch-overflow.ui-native-fixed .ui-footer {
- position: fixed;
- left: 0;
- right: 0;
- top: 0;
- z-index: 200;
-}
-.ui-mobile-touch-overflow.ui-page.ui-native-fixed .ui-footer {
- top: auto;
- bottom: 0;
-}
-.ui-mobile-touch-overflow.ui-native-fixed .ui-content {
- padding-top: 2.5em;
- padding-bottom: 3em;
- top: 0;
- bottom: 0;
- height: auto;
- position: absolute;
-}
-.ui-mobile-touch-overflow.ui-native-fullscreen .ui-content {
- padding-top: 0;
- padding-bottom: 0;
-}
-.ui-mobile-touch-overflow.ui-native-fullscreen .ui-header,
-.ui-mobile-touch-overflow.ui-native-fullscreen .ui-footer {
- opacity: .9;
-}
-.ui-native-bars-hidden {
- display: none;
-}
-
-/* icons sizing */
-.ui-icon { width: 18px; height: 18px; }
-
-/* fullscreen class on ui-content div */
-.ui-fullscreen { }
-.ui-fullscreen img { max-width: 100%; }
-
-/* non-js content hiding */
-.ui-nojs { position: absolute; left: -9999px; }
-/*
-* jQuery Mobile Framework
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
-*/
-.spin {
- -webkit-transform: rotate(360deg);
- -webkit-animation-name: spin;
- -webkit-animation-duration: 1s;
- -webkit-animation-iteration-count: infinite;
- -webkit-animation-timing-function: linear;
-}
-@-webkit-keyframes spin {
- from {-webkit-transform: rotate(0deg);}
- to {-webkit-transform: rotate(360deg);}
-}
-
-/* Transitions from jQtouch (with small modifications): http://www.jqtouch.com/
-Built by David Kaneda and maintained by Jonathan Stark.
-*/
-.in, .out {
- -webkit-animation-timing-function: ease-in-out;
- -webkit-animation-duration: 350ms;
-}
-
-.slide.in {
- -webkit-transform: translateX(0);
- -webkit-animation-name: slideinfromright;
-}
-
-.slide.out {
- -webkit-transform: translateX(-100%);
- -webkit-animation-name: slideouttoleft;
-}
-
-.slide.in.reverse {
- -webkit-transform: translateX(0);
- -webkit-animation-name: slideinfromleft;
-}
-
-.slide.out.reverse {
- -webkit-transform: translateX(100%);
- -webkit-animation-name: slideouttoright;
-}
-
-.slideup.in {
- -webkit-transform: translateY(0);
- -webkit-animation-name: slideinfrombottom;
- z-index: 10;
-}
-
-.slideup.out {
- -webkit-animation-name: dontmove;
- z-index: 0;
-}
-
-.slideup.out.reverse {
- -webkit-transform: translateY(100%);
- z-index: 10;
- -webkit-animation-name: slideouttobottom;
-}
-
-.slideup.in.reverse {
- z-index: 0;
- -webkit-animation-name: dontmove;
-}
-.slidedown.in {
- -webkit-transform: translateY(0);
- -webkit-animation-name: slideinfromtop;
- z-index: 10;
-}
-
-.slidedown.out {
- -webkit-animation-name: dontmove;
- z-index: 0;
-}
-
-.slidedown.out.reverse {
- -webkit-transform: translateY(-100%);
- z-index: 10;
- -webkit-animation-name: slideouttotop;
-}
-
-.slidedown.in.reverse {
- z-index: 0;
- -webkit-animation-name: dontmove;
-}
-
-@-webkit-keyframes slideinfromright {
- from { -webkit-transform: translateX(100%); }
- to { -webkit-transform: translateX(0); }
-}
-
-@-webkit-keyframes slideinfromleft {
- from { -webkit-transform: translateX(-100%); }
- to { -webkit-transform: translateX(0); }
-}
-
-@-webkit-keyframes slideouttoleft {
- from { -webkit-transform: translateX(0); }
- to { -webkit-transform: translateX(-100%); }
-}
-
-@-webkit-keyframes slideouttoright {
- from { -webkit-transform: translateX(0); }
- to { -webkit-transform: translateX(100%); }
-}
-
-
-@-webkit-keyframes slideinfromtop {
- from { -webkit-transform: translateY(-100%); }
- to { -webkit-transform: translateY(0); }
-}
-
-@-webkit-keyframes slideinfrombottom {
- from { -webkit-transform: translateY(100%); }
- to { -webkit-transform: translateY(0); }
-}
-
-@-webkit-keyframes slideouttobottom {
- from { -webkit-transform: translateY(0); }
- to { -webkit-transform: translateY(100%); }
-}
-
-@-webkit-keyframes slideouttotop {
- from { -webkit-transform: translateY(0); }
- to { -webkit-transform: translateY(-100%); }
-}
-@-webkit-keyframes fadein {
- from { opacity: 0; }
- to { opacity: 1; }
-}
-
-@-webkit-keyframes fadeout {
- from { opacity: 1; }
- to { opacity: 0; }
-}
-
-.fade.in {
- opacity: 1;
- z-index: 10;
- -webkit-animation-name: fadein;
-}
-.fade.out {
- z-index: 0;
- -webkit-animation-name: fadeout;
-}
-
-/* The properties in this rule are only necessary for the 'flip' transition.
- * We need specify the perspective to create a projection matrix. This will add
- * some depth as the element flips. The depth number represents the distance of
- * the viewer from the z-plane. According to the CSS3 spec, 1000 is a moderate
- * value.
- */
-.viewport-flip {
- -webkit-perspective: 1000;
- position: absolute;
-}
-
-.ui-mobile-viewport-transitioning,
-.ui-mobile-viewport-transitioning .ui-page {
- width: 100%;
- height: 100%;
- overflow: hidden;
-}
-
-.flip {
- -webkit-animation-duration: .65s;
- -webkit-backface-visibility:hidden;
- -webkit-transform:translateX(0); /* Needed to work around an iOS 3.1 bug that causes listview thumbs to disappear when -webkit-visibility:hidden is used. */
-}
-
-.flip.in {
- -webkit-transform: rotateY(0) scale(1);
- -webkit-animation-name: flipinfromleft;
-}
-
-.flip.out {
- -webkit-transform: rotateY(-180deg) scale(.8);
- -webkit-animation-name: flipouttoleft;
-}
-
-/* Shake it all about */
-
-.flip.in.reverse {
- -webkit-transform: rotateY(0) scale(1);
- -webkit-animation-name: flipinfromright;
-}
-
-.flip.out.reverse {
- -webkit-transform: rotateY(180deg) scale(.8);
- -webkit-animation-name: flipouttoright;
-}
-
-@-webkit-keyframes flipinfromright {
- from { -webkit-transform: rotateY(-180deg) scale(.8); }
- to { -webkit-transform: rotateY(0) scale(1); }
-}
-
-@-webkit-keyframes flipinfromleft {
- from { -webkit-transform: rotateY(180deg) scale(.8); }
- to { -webkit-transform: rotateY(0) scale(1); }
-}
-
-@-webkit-keyframes flipouttoleft {
- from { -webkit-transform: rotateY(0) scale(1); }
- to { -webkit-transform: rotateY(-180deg) scale(.8); }
-}
-
-@-webkit-keyframes flipouttoright {
- from { -webkit-transform: rotateY(0) scale(1); }
- to { -webkit-transform: rotateY(180deg) scale(.8); }
-}
-
-
-/* Hackish, but reliable. */
-
-@-webkit-keyframes dontmove {
- from { opacity: 1; }
- to { opacity: 1; }
-}
-
-.pop {
- -webkit-transform-origin: 50% 50%;
-}
-
-.pop.in {
- -webkit-transform: scale(1);
- opacity: 1;
- -webkit-animation-name: popin;
- z-index: 10;
-}
-
-.pop.out.reverse {
- -webkit-transform: scale(.2);
- opacity: 0;
- -webkit-animation-name: popout;
- z-index: 10;
-}
-
-.pop.in.reverse {
- z-index: 0;
- -webkit-animation-name: dontmove;
-}
-
-@-webkit-keyframes popin {
- from {
- -webkit-transform: scale(.2);
- opacity: 0;
- }
- to {
- -webkit-transform: scale(1);
- opacity: 1;
- }
-}
-
-@-webkit-keyframes popout {
- from {
- -webkit-transform: scale(1);
- opacity: 1;
- }
- to {
- -webkit-transform: scale(.2);
- opacity: 0;
- }
-}/*
-* jQuery Mobile Framework
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
-*/
-
-/* content configurations. */
-.ui-grid-a, .ui-grid-b, .ui-grid-c, .ui-grid-d { overflow: hidden; }
-.ui-block-a, .ui-block-b, .ui-block-c, .ui-block-d, .ui-block-e { margin: 0; padding: 0; border: 0; float: left; min-height:1px;}
-
-/* grid solo: 100 - single item fallback */
-.ui-grid-solo .ui-block-a { width: 100%; float: none; }
-
-/* grid a: 50/50 */
-.ui-grid-a .ui-block-a, .ui-grid-a .ui-block-b { width: 50%; }
-.ui-grid-a .ui-block-a { clear: left; }
-
-/* grid b: 33/33/33 */
-.ui-grid-b .ui-block-a, .ui-grid-b .ui-block-b, .ui-grid-b .ui-block-c { width: 33.333%; }
-.ui-grid-b .ui-block-a { clear: left; }
-
-/* grid c: 25/25/25/25 */
-.ui-grid-c .ui-block-a, .ui-grid-c .ui-block-b, .ui-grid-c .ui-block-c, .ui-grid-c .ui-block-d { width: 25%; }
-.ui-grid-c .ui-block-a { clear: left; }
-
-/* grid d: 20/20/20/20/20 */
-.ui-grid-d .ui-block-a, .ui-grid-d .ui-block-b, .ui-grid-d .ui-block-c, .ui-grid-d .ui-block-d, .ui-grid-d .ui-block-e { width: 20%; }
-.ui-grid-d .ui-block-a { clear: left; }
-/*
-* jQuery Mobile Framework
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
-*/
-/* fixed page header & footer configuration */
-.ui-header, .ui-footer, .ui-page-fullscreen .ui-header, .ui-page-fullscreen .ui-footer { position: absolute; overflow: hidden; width: 100%; border-left-width: 0; border-right-width: 0; }
-.ui-header-fixed, .ui-footer-fixed {
- z-index: 1000;
- -webkit-transform: translateZ(0); /* Force header/footer rendering to go through the same rendering pipeline as native page scrolling. */
-}
-.ui-footer-duplicate, .ui-page-fullscreen .ui-fixed-inline { display: none; }
-.ui-page-fullscreen .ui-header, .ui-page-fullscreen .ui-footer { opacity: .9; }
-/*
-* jQuery Mobile Framework
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
-*/
-.ui-navbar { overflow: hidden; }
-.ui-navbar ul, .ui-navbar-expanded ul { list-style:none; padding: 0; margin: 0; position: relative; display: block; border: 0;}
-.ui-navbar-collapsed ul { float: left; width: 75%; margin-right: -2px; }
-.ui-navbar-collapsed .ui-navbar-toggle { float: left; width: 25%; }
-.ui-navbar li.ui-navbar-truncate { position: absolute; left: -9999px; top: -9999px; }
-.ui-navbar li .ui-btn, .ui-navbar .ui-navbar-toggle .ui-btn { display: block; font-size: 12px; text-align: center; margin: 0; border-right-width: 0; }
-.ui-navbar li .ui-btn { margin-right: -1px; }
-.ui-navbar li .ui-btn:last-child { margin-right: 0; }
-.ui-header .ui-navbar li .ui-btn, .ui-header .ui-navbar .ui-navbar-toggle .ui-btn,
-.ui-footer .ui-navbar li .ui-btn, .ui-footer .ui-navbar .ui-navbar-toggle .ui-btn { border-top-width: 0; border-bottom-width: 0; }
-.ui-navbar .ui-btn-inner { padding-left: 2px; padding-right: 2px; }
-.ui-navbar-noicons li .ui-btn .ui-btn-inner, .ui-navbar-noicons .ui-navbar-toggle .ui-btn-inner { padding-top: .8em; padding-bottom: .9em; }
-/*expanded page styles*/
-.ui-navbar-expanded .ui-btn { margin: 0; font-size: 14px; }
-.ui-navbar-expanded .ui-btn-inner { padding-left: 5px; padding-right: 5px; }
-.ui-navbar-expanded .ui-btn-icon-top .ui-btn-inner { padding: 45px 5px 15px; text-align: center; }
-.ui-navbar-expanded .ui-btn-icon-top .ui-icon { top: 15px; }
-.ui-navbar-expanded .ui-btn-icon-bottom .ui-btn-inner { padding: 15px 5px 45px; text-align: center; }
-.ui-navbar-expanded .ui-btn-icon-bottom .ui-icon { bottom: 15px; }
-.ui-navbar-expanded li .ui-btn .ui-btn-inner { min-height: 2.5em; }
-.ui-navbar-expanded .ui-navbar-noicons .ui-btn .ui-btn-inner { padding-top: 1.8em; padding-bottom: 1.9em; }
-/*
-* jQuery Mobile Framework
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
-*/
-.ui-btn { display: block; text-align: center; cursor:pointer; position: relative; margin: .5em 5px; padding: 0; }
-.ui-btn:focus, .ui-btn:active { outline: none; }
-.ui-header .ui-btn, .ui-footer .ui-btn, .ui-bar .ui-btn { display: inline-block; font-size: 13px; margin: 0; }
-.ui-btn-inline { display: inline-block; }
-.ui-btn-inner { padding: .6em 25px; display: block; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; position: relative; zoom: 1; }
-.ui-header .ui-btn-inner, .ui-footer .ui-btn-inner, .ui-bar .ui-btn-inner { padding: .4em 8px .5em; }
-.ui-btn-icon-notext { display: inline-block; width: 20px; height: 20px; padding: 2px 1px 2px 3px; text-indent: -9999px; }
-.ui-btn-icon-notext .ui-btn-inner { padding: 0; }
-.ui-btn-icon-notext .ui-btn-text { position: absolute; left: -999px; }
-.ui-btn-icon-left .ui-btn-inner { padding-left: 33px; }
-.ui-header .ui-btn-icon-left .ui-btn-inner,
-.ui-footer .ui-btn-icon-left .ui-btn-inner,
-.ui-bar .ui-btn-icon-left .ui-btn-inner { padding-left: 27px; }
-.ui-btn-icon-right .ui-btn-inner { padding-right: 33px; }
-.ui-header .ui-btn-icon-right .ui-btn-inner,
-.ui-footer .ui-btn-icon-right .ui-btn-inner,
-.ui-bar .ui-btn-icon-right .ui-btn-inner { padding-right: 27px; }
-.ui-btn-icon-top .ui-btn-inner { padding-top: 33px; }
-.ui-header .ui-btn-icon-top .ui-btn-inner,
-.ui-footer .ui-btn-icon-top .ui-btn-inner,
-.ui-bar .ui-btn-icon-top .ui-btn-inner { padding-top: 27px; }
-.ui-btn-icon-bottom .ui-btn-inner { padding-bottom: 33px; }
-.ui-header .ui-btn-icon-bottom .ui-btn-inner,
-.ui-footer .ui-btn-icon-bottom .ui-btn-inner,
-.ui-bar .ui-btn-icon-bottom .ui-btn-inner { padding-bottom: 27px; }
-
-/*btn icon positioning*/
-.ui-btn-icon-notext .ui-icon { display: block; }
-.ui-btn-icon-left .ui-icon, .ui-btn-icon-right .ui-icon { position: absolute; top: 50%; margin-top: -9px; }
-.ui-btn-icon-top .ui-icon, .ui-btn-icon-bottom .ui-icon { position: absolute; left: 50%; margin-left: -9px; }
-.ui-btn-icon-left .ui-icon { left: 10px; }
-.ui-btn-icon-right .ui-icon {right: 10px; }
-.ui-header .ui-btn-icon-left .ui-icon,
-.ui-footer .ui-btn-icon-left .ui-icon,
-.ui-bar .ui-btn-icon-left .ui-icon { left: 4px; }
-.ui-header .ui-btn-icon-right .ui-icon,
-.ui-footer .ui-btn-icon-right .ui-icon,
-.ui-bar .ui-btn-icon-right .ui-icon { right: 4px; }
-.ui-header .ui-btn-icon-top .ui-icon,
-.ui-footer .ui-btn-icon-top .ui-icon,
-.ui-bar .ui-btn-icon-top .ui-icon { top: 4px; }
-.ui-header .ui-btn-icon-bottom .ui-icon,
-.ui-footer .ui-btn-icon-bottom .ui-icon,
-.ui-bar .ui-btn-icon-bottom .ui-icon { bottom: 4px; }
-.ui-btn-icon-top .ui-icon { top: 5px; }
-.ui-btn-icon-bottom .ui-icon { bottom: 5px; }
-/*hiding native button,inputs */
-.ui-btn-hidden { position: absolute; top: 0; left: 0; width: 100%; height: 100%; -webkit-appearance: button; opacity: 0; cursor: pointer; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter: alpha(opacity=0); background: transparent; }
-/*
-* jQuery Mobile Framework
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
-*/
-.ui-collapsible-contain { margin: .5em 0; }
-.ui-collapsible-heading { font-size: 16px; display: block; margin: 0 -8px; padding: 0; border-width: 0 0 1px 0; position: relative; }
-.ui-collapsible-heading a { text-align: left; margin: 0; }
-.ui-collapsible-heading a .ui-btn-inner { padding-left: 40px; }
-.ui-collapsible-heading a span.ui-btn { position: absolute; left: 6px; top: 50%; margin: -12px 0 0 0; width: 20px; height: 20px; padding: 1px 0px 1px 2px; text-indent: -9999px; }
-.ui-collapsible-heading a span.ui-btn .ui-btn-inner { padding: 10px 0; }
-.ui-collapsible-heading a span.ui-btn .ui-icon { left: 0; margin-top: -10px; }
-.ui-collapsible-heading-status { position:absolute; left:-9999px; }
-.ui-collapsible-content { display: block; padding: 10px 0 10px 8px; }
-.ui-collapsible-content-collapsed { display: none; }
-
-.ui-collapsible-set { margin: .5em 0; }
-.ui-collapsible-set .ui-collapsible-contain { margin: -1px 0 0; }
-/*
-* jQuery Mobile Framework
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
-*/
-.ui-controlgroup, fieldset.ui-controlgroup { padding: 0; margin: .5em 0 1em; }
-.ui-bar .ui-controlgroup { margin: 0 .3em; }
-.ui-controlgroup-label { font-size: 16px; line-height: 1.4; font-weight: normal; margin: 0 0 .3em; }
-.ui-controlgroup-controls { display: block; width: 95%;}
-.ui-controlgroup li { list-style: none; }
-.ui-controlgroup-vertical .ui-btn,
-.ui-controlgroup-vertical .ui-checkbox, .ui-controlgroup-vertical .ui-radio { margin: 0; border-bottom-width: 0; }
-.ui-controlgroup-vertical .ui-controlgroup-last { border-bottom-width: 1px; }
-.ui-controlgroup-horizontal { padding: 0; }
-.ui-controlgroup-horizontal .ui-btn,
-.ui-controlgroup-horizontal .ui-checkbox, .ui-controlgroup-horizontal .ui-radio { display: inline-block; margin: 0 -5px 0 0; }
-.ui-controlgroup-horizontal .ui-checkbox, .ui-controlgroup-horizontal .ui-radio { display: inline; }
-.ui-controlgroup-horizontal .ui-checkbox .ui-btn, .ui-controlgroup-horizontal .ui-radio .ui-btn,
-.ui-controlgroup-horizontal .ui-checkbox:last-child, .ui-controlgroup-horizontal .ui-radio:last-child { margin-right: 0; }
-.ui-controlgroup-horizontal .ui-controlgroup-last { margin-right: 0; }
-.ui-controlgroup .ui-checkbox label, .ui-controlgroup .ui-radio label { font-size: 16px; }
-/* conflicts with listview..
-.ui-controlgroup .ui-btn-icon-notext { width: 30px; height: 30px; text-indent: -9999px; }
-.ui-controlgroup .ui-btn-icon-notext .ui-btn-inner { padding: 5px 6px 5px 5px; }
-*/
-
-@media all and (min-width: 450px){
- .ui-controlgroup-label { vertical-align: top; display: inline-block; width: 20%; margin: 0 2% 0 0; }
- .ui-controlgroup-controls { width: 60%; display: inline-block; }
-} /*
-* jQuery Mobile Framework
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
-*/
-.ui-dialog { min-height: 480px; }
-.ui-dialog .ui-header, .ui-dialog .ui-content, .ui-dialog .ui-footer { margin: 15px; position: relative; }
-.ui-dialog .ui-header, .ui-dialog .ui-footer { z-index: 10; width: auto; }
-.ui-dialog .ui-content, .ui-dialog .ui-footer { margin-top: -15px; }/*
-* jQuery Mobile Framework
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
-*/
-.ui-checkbox, .ui-radio { position:relative; margin: .2em 0 .5em; z-index: 1; }
-.ui-checkbox .ui-btn, .ui-radio .ui-btn { margin: 0; text-align: left; z-index: 2; }
-.ui-checkbox .ui-btn-inner, .ui-radio .ui-btn-inner { white-space: normal; }
-.ui-checkbox .ui-btn-icon-left .ui-btn-inner,.ui-radio .ui-btn-icon-left .ui-btn-inner { padding-left: 45px; }
-.ui-checkbox .ui-btn-icon-right .ui-btn-inner, .ui-radio .ui-btn-icon-right .ui-btn-inner { padding-right: 45px; }
-.ui-checkbox .ui-icon, .ui-radio .ui-icon { top: 1.1em; }
-.ui-checkbox .ui-btn-icon-left .ui-icon, .ui-radio .ui-btn-icon-left .ui-icon {left: 15px; }
-.ui-checkbox .ui-btn-icon-right .ui-icon, .ui-radio .ui-btn-icon-right .ui-icon {right: 15px; }
-/* input, label positioning */
-.ui-checkbox input,.ui-radio input { position:absolute; left:20px; top:50%; width: 10px; height: 10px; margin:-5px 0 0 0; outline: 0 !important; z-index: 1; }/*
-* jQuery Mobile Framework
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
-*/
-.ui-field-contain { padding: 1.5em 0; margin: 0; border-bottom-width: 1px; overflow: visible; }
-.ui-field-contain:first-child { border-top-width: 0; }
-@media all and (min-width: 450px){
- .ui-field-contain { border-width: 0; padding: 0; margin: 1em 0; }
-} /*
-* jQuery Mobile Framework
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
-*/
-.ui-select { display: block; position: relative; }
-.ui-select select { position: absolute; left: -9999px; top: -9999px; }
-.ui-select .ui-btn { overflow: hidden; }
-.ui-select .ui-btn select { cursor: pointer; -webkit-appearance: button; left: 0; top:0; width: 100%; height: 100%; opacity: 0; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter: alpha(opacity=0); }
-@-moz-document url-prefix() {.ui-select .ui-btn select { opacity: 0.0001; }}
-.ui-select .ui-btn select.ui-select-nativeonly { opacity: 1; text-indent: 0; }
-
-.ui-select .ui-btn-icon-right .ui-btn-inner { padding-right: 45px; }
-.ui-select .ui-btn-icon-right .ui-icon { right: 15px; }
-
-/* labels */
-label.ui-select { font-size: 16px; line-height: 1.4; font-weight: normal; margin: 0 0 .3em; display: block; }
-
-/*listbox*/
-.ui-select .ui-btn-text, .ui-selectmenu .ui-btn-text { display: block; min-height: 1em; }
-.ui-select .ui-btn-text { text-overflow: ellipsis; overflow: hidden;}
-
-.ui-selectmenu { position: absolute; padding: 0; z-index: 100 !important; width: 80%; max-width: 350px; padding: 6px; }
-.ui-selectmenu .ui-listview { margin: 0; }
-.ui-selectmenu .ui-btn.ui-li-divider { cursor: default; }
-.ui-selectmenu-hidden { top: -9999px; left: -9999px; }
-.ui-selectmenu-screen { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 99; }
-.ui-screen-hidden, .ui-selectmenu-list .ui-li .ui-icon { display: none; }
-.ui-selectmenu-list .ui-li .ui-icon { display: block; }
-.ui-li.ui-selectmenu-placeholder { display: none; }
-.ui-selectmenu .ui-header .ui-title { margin: 0.6em 46px 0.8em; }
-
-@media all and (min-width: 450px){
- label.ui-select { display: inline-block; width: 20%; margin: 0 2% 0 0; }
- .ui-select { width: 60%; display: inline-block; }
-}
-
-/* when no placeholder is defined in a multiple select, the header height doesn't even extend past the close button. this shim's content in there */
-.ui-selectmenu .ui-header h1:after { content: '.'; visibility: hidden; }/*
-* jQuery Mobile Framework
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
-*/
-label.ui-input-text { font-size: 16px; line-height: 1.4; display: block; font-weight: normal; margin: 0 0 .3em; }
-input.ui-input-text, textarea.ui-input-text { background-image: none; padding: .4em; line-height: 1.4; font-size: 16px; display: block; width: 95%; }
-input.ui-input-text { -webkit-appearance: none; }
-textarea.ui-input-text { height: 50px; -webkit-transition: height 200ms linear; -moz-transition: height 200ms linear; -o-transition: height 200ms linear; transition: height 200ms linear; }
-.ui-input-search { padding: 0 30px; width: 77%; background-position: 8px 50%; background-repeat: no-repeat; position: relative; }
-.ui-input-search input.ui-input-text { border: none; width: 98%; padding: .4em 0; margin: 0; display: block; background: transparent none; outline: 0 !important; }
-.ui-input-search .ui-input-clear { position: absolute; right: 0; top: 50%; margin-top: -14px; }
-.ui-input-search .ui-input-clear-hidden { display: none; }
-
-/* orientation adjustments - incomplete!*/
-@media all and (min-width: 450px){
- label.ui-input-text { vertical-align: top; display: inline-block; width: 20%; margin: 0 2% 0 0 }
- input.ui-input-text,
- textarea.ui-input-text,
- .ui-input-search { width: 60%; display: inline-block; }
- .ui-input-search { width: 50%; }
- .ui-input-search input.ui-input-text { width: 98%; /*echos rule from above*/ }
-}/*
-* jQuery Mobile Framework
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
-*/
-.ui-listview { margin: 0; counter-reset: listnumbering; }
-.ui-content .ui-listview { margin: -15px; }
-.ui-content .ui-listview-inset { margin: 1em 0; }
-.ui-listview, .ui-li { list-style:none; padding:0; }
-.ui-li, .ui-li.ui-field-contain { display: block; margin:0; position: relative; overflow: visible; text-align: left; border-width: 0; border-top-width: 1px; }
-.ui-li .ui-btn-text a.ui-link-inherit { text-overflow: ellipsis; overflow: hidden; white-space: nowrap; }
-.ui-li-divider, .ui-li-static { padding: .5em 15px; font-size: 14px; font-weight: bold; }
-.ui-li-divider { counter-reset: listnumbering; }
-ol.ui-listview .ui-link-inherit:before, ol.ui-listview .ui-li-static:before, .ui-li-dec { font-size: .8em; display: inline-block; padding-right: .3em; font-weight: normal;counter-increment: listnumbering; content: counter(listnumbering) ". "; }
-ol.ui-listview .ui-li-jsnumbering:before { content: "" !important; } /* to avoid chance of duplication */
-.ui-listview-inset .ui-li { border-right-width: 1px; border-left-width: 1px; }
-.ui-li:last-child, .ui-li.ui-field-contain:last-child { border-bottom-width: 1px; }
-.ui-li>.ui-btn-inner { display: block; position: relative; padding: 0; }
-.ui-li .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li { padding: .7em 15px .7em 15px; display: block; }
-.ui-li-has-thumb .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-thumb { min-height: 60px; padding-left: 100px; }
-.ui-li-has-icon .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-icon { min-height: 20px; padding-left: 40px; }
-.ui-li-has-count .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-count { padding-right: 45px; }
-.ui-li-has-arrow .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-arrow { padding-right: 30px; }
-.ui-li-has-arrow.ui-li-has-count .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-arrow.ui-li-has-count { padding-right: 75px; }
-.ui-li-heading { font-size: 16px; font-weight: bold; display: block; margin: .6em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; }
-.ui-li-desc { font-size: 12px; font-weight: normal; display: block; margin: -.5em 0 .6em; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; }
-.ui-li-thumb, .ui-li-icon { position: absolute; left: 1px; top: 0; max-height: 80px; max-width: 80px; }
-.ui-li-icon { max-height: 40px; max-width: 40px; left: 10px; top: .9em; }
-.ui-li-thumb, .ui-li-icon, .ui-li-content { float: left; margin-right: 10px; }
-
-.ui-li-aside { float: right; width: 50%; text-align: right; margin: .3em 0; }
-@media all and (min-width: 480px){
- .ui-li-aside { width: 45%; }
-}
-.ui-li-divider { cursor: default; }
-.ui-li-has-alt .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-alt { padding-right: 95px; }
-.ui-li-count { position: absolute; font-size: 11px; font-weight: bold; padding: .2em .5em; top: 50%; margin-top: -.9em; right: 38px; }
-.ui-li-divider .ui-li-count, .ui-li-static .ui-li-count { right: 10px; }
-.ui-li-has-alt .ui-li-count { right: 55px; }
-.ui-li-link-alt { position: absolute; width: 40px; height: 100%; border-width: 0; border-left-width: 1px; top: 0; right: 0; margin: 0; padding: 0; }
-.ui-li-link-alt .ui-btn { overflow: hidden; position: absolute; right: 8px; top: 50%; margin: -11px 0 0 0; border-bottom-width: 1px; }
-.ui-li-link-alt .ui-btn-inner { padding: 0; position: static; }
-.ui-li-link-alt .ui-btn .ui-icon { right: 50%; margin-right: -9px; }
-
-.ui-listview-filter { border-width: 0; overflow: hidden; margin: -15px -15px 15px -15px }
-.ui-listview-filter .ui-input-search { margin: 5px; width: auto; display: block; }
-
-.ui-listview-filter-inset { margin: -15px -5px -15px -5px; background: transparent; }
-.ui-li.ui-screen-hidden{display:none;}
-/* Odd iPad positioning issue. */
-@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) {
- .ui-li .ui-btn-text { overflow: visible; }
-}/*
-* jQuery Mobile Framework
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
-*/
-label.ui-slider { display: block; }
-input.ui-slider-input { display: inline-block; width: 50px; }
-select.ui-slider-switch { display: none; }
-div.ui-slider { position: relative; display: inline-block; overflow: visible; height: 15px; padding: 0; margin: 0 2% 0 20px; top: 4px; width: 66%; }
-a.ui-slider-handle { position: absolute; z-index: 10; top: 50%; width: 28px; height: 28px; margin-top: -15px; margin-left: -15px; }
-a.ui-slider-handle .ui-btn-inner { padding-left: 0; padding-right: 0; }
-@media all and (min-width: 480px){
- label.ui-slider { display: inline-block; width: 20%; margin: 0 2% 0 0; }
- div.ui-slider { width: 45%; }
-}
-
-div.ui-slider-switch { height: 32px; overflow: hidden; margin-left: 0; }
-div.ui-slider-inneroffset { margin-left: 50%; position: absolute; top: 1px; height: 100%; width: 50%; }
-a.ui-slider-handle-snapping { -webkit-transition: left 100ms linear; }
-div.ui-slider-labelbg { position: absolute; top:0; margin: 0; border-width: 0; }
-div.ui-slider-switch div.ui-slider-labelbg-a { width: 60%; height: 100%; left: 0; }
-div.ui-slider-switch div.ui-slider-labelbg-b { width: 60%; height: 100%; right: 0; }
-.ui-slider-switch-a div.ui-slider-labelbg-a, .ui-slider-switch-b div.ui-slider-labelbg-b { z-index: -1; }
-.ui-slider-switch-a div.ui-slider-labelbg-b, .ui-slider-switch-b div.ui-slider-labelbg-a { z-index: 0; }
-
-div.ui-slider-switch a.ui-slider-handle { z-index: 20; width: 101%; height: 32px; margin-top: -18px; margin-left: -101%; }
-span.ui-slider-label { width: 100%; position: absolute;height: 32px; font-size: 16px; text-align: center; line-height: 2; background: none; border-color: transparent; }
-span.ui-slider-label-a { left: -100%; margin-right: -1px }
-span.ui-slider-label-b { right: -100%; margin-left: -1px }
-
--- /dev/null
+++ b/css/jquery.mobile-1.0rc1.css
@@ -1,1 +1,1750 @@
-
+/*!
+ * jQuery Mobile v1.0rc1
+ * http://jquerymobile.com/
+ *
+ * Copyright 2010, jQuery Project
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ */
+/*!
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+*/
+
+/* Swatches */
+
+/* A
+-----------------------------------------------------------------------------------------------------------*/
+
+.ui-bar-a {
+ border: 1px solid #2A2A2A /*{a-bar-border}*/;
+ background: #111111 /*{a-bar-background-color}*/;
+ color: #ffffff /*{a-bar-color}*/;
+ font-weight: bold;
+ text-shadow: 0 /*{a-bar-shadow-x}*/ -1px /*{a-bar-shadow-y}*/ 1px /*{a-bar-shadow-radius}*/ #000000 /*{a-bar-shadow-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c /*{a-bar-background-start}*/), to(#111 /*{a-bar-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #3c3c3c /*{a-bar-background-start}*/, #111 /*{a-bar-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #3c3c3c /*{a-bar-background-start}*/, #111 /*{a-bar-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #3c3c3c /*{a-bar-background-start}*/, #111 /*{a-bar-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #3c3c3c /*{a-bar-background-start}*/, #111 /*{a-bar-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #3c3c3c /*{a-bar-background-start}*/, #111 /*{a-bar-background-end}*/);
+}
+.ui-bar-a,
+.ui-bar-a input,
+.ui-bar-a select,
+.ui-bar-a textarea,
+.ui-bar-a button {
+ font-family: Helvetica, Arial, sans-serif /*{a-bar-font}*/;
+}
+.ui-bar-a .ui-link-inherit {
+ color: #fff /*{a-bar-color}*/;
+}
+.ui-bar-a .ui-link {
+ color: #7cc4e7 /*{global-link-color}*/;
+ font-weight: bold;
+}
+.ui-body-a {
+ border: 1px solid #2A2A2A /*{a-body-border}*/;
+ background: #222222 /*{a-body-background-color}*/;
+ color: #fff /*{a-body-color}*/;
+ text-shadow: 0 /*{a-body-shadow-x}*/ 1px /*{a-body-shadow-y}*/ 0 /*{a-body-shadow-radius}*/ #000 /*{a-body-shadow-color}*/;
+ font-weight: normal;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#666 /*{a-body-background-start}*/), to(#222 /*{a-body-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #666 /*{a-body-background-start}*/, #222 /*{a-body-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #666 /*{a-body-background-start}*/, #222 /*{a-body-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #666 /*{a-body-background-start}*/, #222 /*{a-body-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #666 /*{a-body-background-start}*/, #222 /*{a-body-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #666 /*{a-body-background-start}*/, #222 /*{a-body-background-end}*/);
+}
+.ui-body-a,
+.ui-body-a input,
+.ui-body-a select,
+.ui-body-a textarea,
+.ui-body-a button {
+ font-family: Helvetica, Arial, sans-serif /*{a-body-font}*/;
+}
+.ui-body-a .ui-link-inherit {
+ color: #fff /*{a-body-color}*/;
+}
+.ui-body-a .ui-link {
+ color: #2489CE /*{global-link-color}*/;
+ font-weight: bold;
+}
+.ui-br {
+ border-bottom: rgb(130,130,130);
+ border-bottom: rgba(130,130,130,.3);
+ border-bottom-width: 1px;
+ border-bottom-style: solid;
+}
+.ui-btn-up-a {
+ border: 1px solid #222 /*{a-bup-border}*/;
+ background: #333333 /*{a-bup-background-color}*/;
+ font-weight: bold;
+ color: #fff /*{a-bup-color}*/;
+ text-shadow: 0 /*{a-bup-shadow-x}*/ -1px /*{a-bup-shadow-y}*/ 1px /*{a-bup-shadow-radius}*/ #000 /*{a-bup-shadow-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#555 /*{a-bup-background-start}*/), to(#333 /*{a-bup-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #555 /*{a-bup-background-start}*/, #333 /*{a-bup-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #555 /*{a-bup-background-start}*/, #333 /*{a-bup-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #555 /*{a-bup-background-start}*/, #333 /*{a-bup-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #555 /*{a-bup-background-start}*/, #333 /*{a-bup-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #555 /*{a-bup-background-start}*/, #333 /*{a-bup-background-end}*/);
+}
+.ui-btn-up-a a.ui-link-inherit {
+ color: #fff /*{a-bup-color}*/;
+}
+.ui-btn-hover-a {
+ border: 1px solid #000 /*{a-bhover-border}*/;
+ background: #444444 /*{a-bhover-background-color}*/;
+ font-weight: bold;
+ color: #fff /*{a-bhover-color}*/;
+ text-shadow: 0 /*{a-bhover-shadow-x}*/ -1px /*{a-bhover-shadow-y}*/ 1px /*{a-bhover-shadow-radius}*/ #000 /*{a-bhover-shadow-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#666 /*{a-bhover-background-start}*/), to(#444 /*{a-bhover-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #666 /*{a-bhover-background-start}*/, #444 /*{a-bhover-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #666 /*{a-bhover-background-start}*/, #444 /*{a-bhover-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #666 /*{a-bhover-background-start}*/, #444 /*{a-bhover-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #666 /*{a-bhover-background-start}*/, #444 /*{a-bhover-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #666 /*{a-bhover-background-start}*/, #444 /*{a-bhover-background-end}*/);
+}
+.ui-btn-hover-a a.ui-link-inherit {
+ color: #fff /*{a-bhover-color}*/;
+}
+.ui-btn-down-a {
+ border: 1px solid #000 /*{a-bdown-border}*/;
+ background: #3d3d3d /*{a-bdown-background-color}*/;
+ font-weight: bold;
+ color: #fff /*{a-bdown-color}*/;
+ text-shadow: 0 /*{a-bdown-shadow-x}*/ -1px /*{a-bdown-shadow-y}*/ 1px /*{a-bdown-shadow-radius}*/ #000 /*{a-bdown-shadow-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#333 /*{a-bdown-background-start}*/), to(#5a5a5a /*{a-bdown-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #333 /*{a-bdown-background-start}*/, #5a5a5a /*{a-bdown-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #333 /*{a-bdown-background-start}*/, #5a5a5a /*{a-bdown-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #333 /*{a-bdown-background-start}*/, #5a5a5a /*{a-bdown-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #333 /*{a-bdown-background-start}*/, #5a5a5a /*{a-bdown-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #333 /*{a-bdown-background-start}*/, #5a5a5a /*{a-bdown-background-end}*/);
+}
+.ui-btn-down-a a.ui-link-inherit {
+ color: #fff /*{a-bdown-color}*/;
+}
+.ui-btn-up-a,
+.ui-btn-hover-a,
+.ui-btn-down-a {
+ font-family: Helvetica, Arial, sans-serif /*{a-button-font}*/;
+ text-decoration: none;
+}
+
+
+/* B
+-----------------------------------------------------------------------------------------------------------*/
+
+.ui-bar-b {
+ border: 1px solid #456f9a /*{b-bar-border}*/;
+ background: #5e87b0 /*{b-bar-background-color}*/;
+ color: #fff /*{b-bar-color}*/;
+ font-weight: bold;
+ text-shadow: 0 /*{b-bar-shadow-x}*/ -1px /*{b-bar-shadow-y}*/ 1px /*{b-bar-shadow-radius}*/ #254f7a /*{b-bar-shadow-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#81a8ce /*{b-bar-background-start}*/), to(#5e87b0 /*{b-bar-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #81a8ce /*{b-bar-background-start}*/, #5e87b0 /*{b-bar-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #81a8ce /*{b-bar-background-start}*/, #5e87b0 /*{b-bar-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #81a8ce /*{b-bar-background-start}*/, #5e87b0 /*{b-bar-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #81a8ce /*{b-bar-background-start}*/, #5e87b0 /*{b-bar-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #81a8ce /*{b-bar-background-start}*/, #5e87b0 /*{b-bar-background-end}*/);
+}
+.ui-bar-b,
+.ui-bar-b input,
+.ui-bar-b select,
+.ui-bar-b textarea,
+.ui-bar-b button {
+ font-family: Helvetica, Arial, sans-serif /*{b-bar-font}*/;
+}
+.ui-bar-b .ui-link-inherit {
+ color: #fff /*{b-bar-color}*/;
+}
+.ui-bar-b .ui-link {
+ color: #7cc4e7 /*{global-link-color}*/;
+ font-weight: bold;
+}
+
+.ui-body-b {
+ border: 1px solid #C6C6C6 /*{b-body-border}*/;
+ background: #cccccc /*{b-body-background-color}*/;
+ color: #333333 /*{b-body-color}*/;
+ text-shadow: 0 /*{b-body-shadow-x}*/ 1px /*{b-body-shadow-y}*/ 0 /*{b-body-shadow-radius}*/ #fff /*{b-body-shadow-color}*/;
+ font-weight: normal;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#e6e6e6 /*{b-body-background-start}*/), to(#ccc /*{b-body-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #e6e6e6 /*{b-body-background-start}*/, #ccc /*{b-body-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #e6e6e6 /*{b-body-background-start}*/, #ccc /*{b-body-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #e6e6e6 /*{b-body-background-start}*/, #ccc /*{b-body-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #e6e6e6 /*{b-body-background-start}*/, #ccc /*{b-body-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #e6e6e6 /*{b-body-background-start}*/, #ccc /*{b-body-background-end}*/);
+}
+.ui-body-b,
+.ui-body-b input,
+.ui-body-b select,
+.ui-body-b textarea,
+.ui-body-b button {
+ font-family: Helvetica, Arial, sans-serif /*{b-body-font}*/;
+}
+.ui-body-b .ui-link-inherit {
+ color: #333333 /*{b-body-color}*/;
+}
+.ui-body-b .ui-link {
+ color: #2489CE /*{global-link-color}*/;
+ font-weight: bold;
+}
+.ui-btn-up-b {
+ border: 1px solid #145072 /*{b-bup-border}*/;
+ background: #2567ab /*{b-bup-background-color}*/;
+ font-weight: bold;
+ color: #fff /*{b-bup-color}*/;
+ text-shadow: 0 /*{b-bup-shadow-x}*/ -1px /*{b-bup-shadow-y}*/ 1px /*{b-bup-shadow-radius}*/ #145072 /*{b-bup-shadow-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#5f9cc5 /*{b-bup-background-start}*/), to(#396b9e /*{b-bup-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #5f9cc5 /*{b-bup-background-start}*/, #396b9e /*{b-bup-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #5f9cc5 /*{b-bup-background-start}*/, #396b9e /*{b-bup-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #5f9cc5 /*{b-bup-background-start}*/, #396b9e /*{b-bup-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #5f9cc5 /*{b-bup-background-start}*/, #396b9e /*{b-bup-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #5f9cc5 /*{b-bup-background-start}*/, #396b9e /*{b-bup-background-end}*/);
+}
+.ui-btn-up-b a.ui-link-inherit {
+ color: #fff /*{b-bup-color}*/;
+}
+.ui-btn-hover-b {
+ border: 1px solid #00516e /*{b-bhover-border}*/;
+ background: #4b88b6 /*{b-bhover-background-color}*/;
+ font-weight: bold;
+ color: #fff /*{b-bhover-color}*/;
+ text-shadow: 0 /*{b-bhover-shadow-x}*/ -1px /*{b-bhover-shadow-y}*/ 1px /*{b-bhover-shadow-radius}*/ #014D68 /*{b-bhover-shadow-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#72b0d4 /*{b-bhover-background-start}*/), to(#4b88b6 /*{b-bhover-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #72b0d4 /*{b-bhover-background-start}*/, #4b88b6 /*{b-bhover-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #72b0d4 /*{b-bhover-background-start}*/, #4b88b6 /*{b-bhover-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #72b0d4 /*{b-bhover-background-start}*/, #4b88b6 /*{b-bhover-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #72b0d4 /*{b-bhover-background-start}*/, #4b88b6 /*{b-bhover-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #72b0d4 /*{b-bhover-background-start}*/, #4b88b6 /*{b-bhover-background-end}*/);
+}
+.ui-btn-hover-b a.ui-link-inherit {
+ color: #fff /*{b-bhover-color}*/;
+}
+.ui-btn-down-b {
+ border: 1px solid #225377 /*{b-bdown-border}*/;
+ background: #4e89c5 /*{b-bdown-background-color}*/;
+ font-weight: bold;
+ color: #fff /*{b-bdown-color}*/;
+ text-shadow: 0 /*{b-bdown-shadow-x}*/ -1px /*{b-bdown-shadow-y}*/ 1px /*{b-bdown-shadow-radius}*/ #225377 /*{b-bdown-shadow-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#396b9e /*{b-bdown-background-start}*/), to(#4e89c5 /*{b-bdown-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #396b9e /*{b-bdown-background-start}*/, #4e89c5 /*{b-bdown-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #396b9e /*{b-bdown-background-start}*/, #4e89c5 /*{b-bdown-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #396b9e /*{b-bdown-background-start}*/, #4e89c5 /*{b-bdown-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #396b9e /*{b-bdown-background-start}*/, #4e89c5 /*{b-bdown-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #396b9e /*{b-bdown-background-start}*/, #4e89c5 /*{b-bdown-background-end}*/);
+}
+.ui-btn-down-b a.ui-link-inherit {
+ color: #fff /*{b-bdown-color}*/;
+}
+.ui-btn-up-b,
+.ui-btn-hover-b,
+.ui-btn-down-b {
+ font-family: Helvetica, Arial, sans-serif /*{b-button-font}*/;
+ text-decoration: none;
+}
+
+
+/* C
+-----------------------------------------------------------------------------------------------------------*/
+
+.ui-bar-c {
+ border: 1px solid #B3B3B3 /*{c-bar-border}*/;
+ background: #e9eaeb /*{c-bar-background-color}*/;
+ color: #3E3E3E /*{c-bar-color}*/;
+ font-weight: bold;
+ text-shadow: 0 /*{c-bar-shadow-x}*/ 1px /*{c-bar-shadow-y}*/ 1px /*{c-bar-shadow-radius}*/ #fff /*{c-bar-shadow-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#f0f0f0 /*{c-bar-background-start}*/), to(#e9eaeb /*{c-bar-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #f0f0f0 /*{c-bar-background-start}*/, #e9eaeb /*{c-bar-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #f0f0f0 /*{c-bar-background-start}*/, #e9eaeb /*{c-bar-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #f0f0f0 /*{c-bar-background-start}*/, #e9eaeb /*{c-bar-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #f0f0f0 /*{c-bar-background-start}*/, #e9eaeb /*{c-bar-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #f0f0f0 /*{c-bar-background-start}*/, #e9eaeb /*{c-bar-background-end}*/);
+}
+
+.ui-bar-c .ui-link {
+ color: #2489CE /*{global-link-color}*/;
+ font-weight: bold;
+}
+
+.ui-bar-c,
+.ui-bar-c input,
+.ui-bar-c select,
+.ui-bar-c textarea,
+.ui-bar-c button {
+ font-family: Helvetica, Arial, sans-serif /*{c-bar-font}*/;
+}
+.ui-body-c {
+ border: 1px solid #B3B3B3 /*{c-body-border}*/;
+ color: #333333 /*{c-body-color}*/;
+ text-shadow: 0 /*{c-body-shadow-x}*/ 1px /*{c-body-shadow-y}*/ 0 /*{c-body-shadow-radius}*/ #fff /*{c-body-shadow-color}*/;
+ background: #f0f0f0 /*{c-body-background-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#eee /*{c-body-background-start}*/), to(#ddd /*{c-body-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #eee /*{c-body-background-start}*/, #ddd /*{c-body-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #eee /*{c-body-background-start}*/, #ddd /*{c-body-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #eee /*{c-body-background-start}*/, #ddd /*{c-body-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #eee /*{c-body-background-start}*/, #ddd /*{c-body-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #eee /*{c-body-background-start}*/, #ddd /*{c-body-background-end}*/);
+}
+.ui-body-c,
+.ui-body-c input,
+.ui-body-c select,
+.ui-body-c textarea,
+.ui-body-c button {
+ font-family: Helvetica, Arial, sans-serif /*{c-body-font}*/;
+}
+.ui-body-c .ui-link-inherit {
+ color: #333333 /*{c-body-color}*/;
+}
+.ui-body-c .ui-link {
+ color: #2489CE /*{global-link-color}*/;
+ font-weight: bold;
+}
+
+.ui-btn-up-c {
+ border: 1px solid #ccc /*{c-bup-border}*/;
+ background: #eee /*{c-bup-background-color}*/;
+ font-weight: bold;
+ color: #444 /*{c-bup-color}*/;
+ text-shadow: 0 /*{c-bup-shadow-x}*/ 1px /*{c-bup-shadow-y}*/ 1px /*{c-bup-shadow-radius}*/ #f6f6f6 /*{c-bup-shadow-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fdfdfd /*{c-bup-background-start}*/), to(#eee /*{c-bup-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #fdfdfd /*{c-bup-background-start}*/, #eee /*{c-bup-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #fdfdfd /*{c-bup-background-start}*/, #eee /*{c-bup-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #fdfdfd /*{c-bup-background-start}*/, #eee /*{c-bup-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #fdfdfd /*{c-bup-background-start}*/, #eee /*{c-bup-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #fdfdfd /*{c-bup-background-start}*/, #eee /*{c-bup-background-end}*/);
+}
+.ui-btn-up-c a.ui-link-inherit {
+ color: #2F3E46 /*{c-bup-color}*/;
+}
+
+.ui-btn-hover-c {
+ border: 1px solid #bbb /*{c-bhover-border}*/;
+ background: #dadada /*{c-bhover-background-color}*/;
+ font-weight: bold;
+ color: #101010 /*{c-bhover-color}*/;
+ text-shadow: 0 /*{c-bhover-shadow-x}*/ 1px /*{c-bhover-shadow-y}*/ 1px /*{c-bhover-shadow-radius}*/ #fff /*{c-bhover-shadow-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#ededed /*{c-bhover-background-start}*/), to(#dadada /*{c-bhover-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #ededed /*{c-bhover-background-start}*/, #dadada /*{c-bhover-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #ededed /*{c-bhover-background-start}*/, #dadada /*{c-bhover-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #ededed /*{c-bhover-background-start}*/, #dadada /*{c-bhover-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #ededed /*{c-bhover-background-start}*/, #dadada /*{c-bhover-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #ededed /*{c-bhover-background-start}*/, #dadada /*{c-bhover-background-end}*/);
+}
+.ui-btn-hover-c a.ui-link-inherit {
+ color: #2F3E46 /*{c-bhover-color}*/;
+}
+.ui-btn-down-c {
+ border: 1px solid #808080 /*{c-bdown-border}*/;
+ background: #fdfdfd /*{c-bdown-background-color}*/;
+ font-weight: bold;
+ color: #111111 /*{c-bdown-color}*/;
+ text-shadow: 0 /*{c-bdown-shadow-x}*/ 1px /*{c-bdown-shadow-y}*/ 1px /*{c-bdown-shadow-radius}*/ #ffffff /*{c-bdown-shadow-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#eee /*{c-bdown-background-start}*/), to(#fdfdfd /*{c-bdown-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #eee /*{c-bdown-background-start}*/, #fdfdfd /*{c-bdown-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #eee /*{c-bdown-background-start}*/, #fdfdfd /*{c-bdown-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #eee /*{c-bdown-background-start}*/, #fdfdfd /*{c-bdown-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #eee /*{c-bdown-background-start}*/, #fdfdfd /*{c-bdown-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #eee /*{c-bdown-background-start}*/, #fdfdfd /*{c-bdown-background-end}*/);
+}
+.ui-btn-down-c a.ui-link-inherit {
+ color: #2F3E46 /*{c-bdown-color}*/;
+}
+.ui-btn-up-c,
+.ui-btn-hover-c,
+.ui-btn-down-c {
+ font-family: Helvetica, Arial, sans-serif /*{c-button-font}*/;
+ text-decoration: none;
+}
+
+
+/* D
+-----------------------------------------------------------------------------------------------------------*/
+
+.ui-bar-d {
+ border: 1px solid #ccc /*{d-bar-border}*/;
+ background: #bbb /*{d-bar-background-color}*/;
+ color: #333 /*{d-bar-color}*/;
+ text-shadow: 0 /*{d-bar-shadow-x}*/ 1px /*{d-bar-shadow-y}*/ 0 /*{d-bar-shadow-radius}*/ #eee /*{d-bar-shadow-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#ddd /*{d-bar-background-start}*/), to(#bbb /*{d-bar-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #ddd /*{d-bar-background-start}*/, #bbb /*{d-bar-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #ddd /*{d-bar-background-start}*/, #bbb /*{d-bar-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #ddd /*{d-bar-background-start}*/, #bbb /*{d-bar-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #ddd /*{d-bar-background-start}*/, #bbb /*{d-bar-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #ddd /*{d-bar-background-start}*/, #bbb /*{d-bar-background-end}*/);
+}
+.ui-bar-d,
+.ui-bar-d input,
+.ui-bar-d select,
+.ui-bar-d textarea,
+.ui-bar-d button {
+ font-family: Helvetica, Arial, sans-serif /*{d-bar-font}*/;
+}
+.ui-bar-d .ui-link-inherit {
+ color: #333 /*{d-bar-color}*/;
+}
+.ui-bar-d .ui-link {
+ color: #2489CE /*{global-link-color}*/;
+ font-weight: bold;
+}
+.ui-body-d {
+ border: 1px solid #ccc /*{d-body-border}*/;
+ color: #333333 /*{d-body-color}*/;
+ text-shadow: 0 /*{d-body-shadow-x}*/ 1px /*{d-body-shadow-y}*/ 0 /*{d-body-shadow-radius}*/ #fff /*{d-body-shadow-color}*/;
+ background: #ffffff /*{d-body-background-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#fff /*{d-body-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #fff /*{d-body-background-start}*/, #fff /*{d-body-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #fff /*{d-body-background-start}*/, #fff /*{d-body-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #fff /*{d-body-background-start}*/, #fff /*{d-body-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #fff /*{d-body-background-start}*/, #fff /*{d-body-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #fff /*{d-body-background-start}*/, #fff /*{d-body-background-end}*/);
+}
+.ui-body-d,
+.ui-body-d input,
+.ui-body-d select,
+.ui-body-d textarea,
+.ui-body-d button {
+ font-family: Helvetica, Arial, sans-serif /*{d-body-font}*/;
+}
+.ui-body-d .ui-link-inherit {
+ color: #333333 /*{d-body-color}*/;
+}
+.ui-body-d .ui-link {
+ color: #2489CE /*{global-link-color}*/;
+ font-weight: bold;
+}
+.ui-btn-up-d {
+ border: 1px solid #ccc /*{d-bup-border}*/;
+ background: #fff /*{d-bup-background-color}*/;
+ font-weight: bold;
+ color: #444 /*{d-bup-color}*/;
+ text-shadow: 0 /*{d-bup-shadow-x}*/ 1px /*{d-bup-shadow-y}*/ 1px /*{d-bup-shadow-radius}*/ #fff /*{d-bup-shadow-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#fff /*{d-bup-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #fff /*{d-bup-background-start}*/, #fff /*{d-bup-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #fff /*{d-bup-background-start}*/, #fff /*{d-bup-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #fff /*{d-bup-background-start}*/, #fff /*{d-bup-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #fff /*{d-bup-background-start}*/, #fff /*{d-bup-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #fff /*{d-bup-background-start}*/, #fff /*{d-bup-background-end}*/);
+}
+.ui-btn-up-d a.ui-link-inherit {
+ color: #333 /*{d-bup-color}*/;
+}
+.ui-btn-hover-d {
+ border: 1px solid #aaa /*{d-bhover-border}*/;
+ background: #eeeeee /*{d-bhover-background-color}*/;
+ font-weight: bold;
+ color: #222 /*{d-bhover-color}*/;
+ cursor: pointer;
+ text-shadow: 0 /*{d-bhover-shadow-x}*/ 1px /*{d-bhover-shadow-y}*/ 1px /*{d-bhover-shadow-radius}*/ #fff /*{d-bhover-shadow-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fdfdfd), to(#eee /*{d-bhover-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #fdfdfd /*{d-bhover-background-start}*/, #eee /*{d-bhover-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #fdfdfd /*{d-bhover-background-start}*/, #eee /*{d-bhover-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #fdfdfd /*{d-bhover-background-start}*/, #eee /*{d-bhover-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #fdfdfd /*{d-bhover-background-start}*/, #eee /*{d-bhover-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #fdfdfd /*{d-bhover-background-start}*/, #eee /*{d-bhover-background-end}*/);
+}
+.ui-btn-hover-d a.ui-link-inherit {
+ color: #222 /*{d-bhover-color}*/;
+}
+.ui-btn-down-d {
+ border: 1px solid #aaaaaa /*{d-bdown-border}*/;
+ background: #ffffff /*{d-bdown-background-color}*/;
+ font-weight: bold;
+ color: #111 /*{d-bdown-color}*/;
+ text-shadow: 0 /*{d-bdown-shadow-x}*/ 1px /*{d-bdown-shadow-y}*/ 1px /*{d-bdown-shadow-radius}*/ #ffffff /*{d-bdown-shadow-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#eee /*{d-bdown-background-start}*/), to(#fff /*{d-bdown-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #eee /*{d-bdown-background-start}*/, #fff /*{d-bdown-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #eee /*{d-bdown-background-start}*/, #fff /*{d-bdown-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #eee /*{d-bdown-background-start}*/, #fff /*{d-bdown-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #eee /*{d-bdown-background-start}*/, #fff /*{d-bdown-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #eee /*{d-bdown-background-start}*/, #fff /*{d-bdown-background-end}*/);
+}
+.ui-btn-down-d a.ui-link-inherit {
+ color: #111 /*{d-bdown-color}*/;
+}
+.ui-btn-up-d,
+.ui-btn-hover-d,
+.ui-btn-down-d {
+ font-family: Helvetica, Arial, sans-serif /*{d-button-font}*/;
+ text-decoration: none;
+}
+
+
+/* E
+-----------------------------------------------------------------------------------------------------------*/
+
+.ui-bar-e {
+ border: 1px solid #F7C942 /*{e-bar-border}*/;
+ background: #fadb4e /*{e-bar-background-color}*/;
+ color: #333 /*{e-bar-color}*/;
+ text-shadow: 0 /*{e-bar-shadow-x}*/ 1px /*{e-bar-shadow-y}*/ 0 /*{e-bar-shadow-radius}*/ #fff /*{e-bar-shadow-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fceda7 /*{e-bar-background-start}*/), to(#fadb4e /*{e-bar-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #fceda7 /*{e-bar-background-start}*/, #fadb4e /*{e-bar-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #fceda7 /*{e-bar-background-start}*/, #fadb4e /*{e-bar-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #fceda7 /*{e-bar-background-start}*/, #fadb4e /*{e-bar-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #fceda7 /*{e-bar-background-start}*/, #fadb4e /*{e-bar-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #fceda7 /*{e-bar-background-start}*/, #fadb4e /*{e-bar-background-end}*/);
+}
+.ui-bar-e,
+.ui-bar-e input,
+.ui-bar-e select,
+.ui-bar-e textarea,
+.ui-bar-e button {
+ font-family: Helvetica, Arial, sans-serif /*{e-bar-font}*/;
+}
+.ui-bar-e .ui-link-inherit {
+ color: #333 /*{e-bar-color}*/;
+}
+.ui-bar-e .ui-link {
+ color: #2489CE /*{global-link-color}*/;
+ font-weight: bold;
+}
+.ui-body-e {
+ border: 1px solid #F7C942 /*{e-body-border}*/;
+ color: #333333 /*{e-body-color}*/;
+ text-shadow: 0 /*{e-body-shadow-x}*/ 1px /*{e-body-shadow-y}*/ 0 /*{e-body-shadow-radius}*/ #fff /*{e-body-shadow-color}*/;
+ background: #faeb9e /*{e-body-background-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fff /*{e-body-background-start}*/), to(#faeb9e /*{e-body-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #fff /*{e-body-background-start}*/, #faeb9e /*{e-body-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #fff /*{e-body-background-start}*/, #faeb9e /*{e-body-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #fff /*{e-body-background-start}*/, #faeb9e /*{e-body-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #fff /*{e-body-background-start}*/, #faeb9e /*{e-body-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #fff /*{e-body-background-start}*/, #faeb9e /*{e-body-background-end}*/);
+}
+.ui-body-e,
+.ui-body-e input,
+.ui-body-e select,
+.ui-body-e textarea,
+.ui-body-e button {
+ font-family: Helvetica, Arial, sans-serif /*{e-body-font}*/;
+}
+.ui-body-e .ui-link-inherit {
+ color: #333333 /*{e-body-color}*/;
+}
+.ui-body-e .ui-link {
+ color: #2489CE /*{global-link-color}*/;
+ font-weight: bold;
+}
+.ui-btn-up-e {
+ border: 1px solid #F7C942 /*{e-bup-border}*/;
+ background: #fadb4e /*{e-bup-background-color}*/;
+ font-weight: bold;
+ color: #333 /*{e-bup-color}*/;
+ text-shadow: 0 /*{e-bup-shadow-x}*/ 1px /*{e-bup-shadow-y}*/ 0 /*{e-bup-shadow-radius}*/ #fff /*{e-bup-shadow-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fceda7 /*{e-bup-background-start}*/), to(#fadb4e /*{e-bup-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #fceda7 /*{e-bup-background-start}*/, #fadb4e /*{e-bup-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #fceda7 /*{e-bup-background-start}*/, #fadb4e /*{e-bup-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #fceda7 /*{e-bup-background-start}*/, #fadb4e /*{e-bup-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #fceda7 /*{e-bup-background-start}*/, #fadb4e /*{e-bup-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #fceda7 /*{e-bup-background-start}*/, #fadb4e /*{e-bup-background-end}*/);
+}
+.ui-btn-up-e a.ui-link-inherit {
+ color: #333 /*{e-bup-color}*/;
+}
+.ui-btn-hover-e {
+ border: 1px solid #e79952 /*{e-bhover-border}*/;
+ background: #fbe26f /*{e-bhover-background-color}*/;
+ font-weight: bold;
+ color: #111 /*{e-bhover-color}*/;
+ text-shadow: 0 /*{e-bhover-shadow-x}*/ 1px /*{e-bhover-shadow-y}*/ 1px /*{e-bhover-shadow-radius}*/ #fff /*{e-bhover-shadow-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf0b5 /*{e-bhover-background-start}*/), to(#fbe26f /*{e-bhover-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #fcf0b5 /*{e-bhover-background-start}*/, #fbe26f /*{e-bhover-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #fcf0b5 /*{e-bhover-background-start}*/, #fbe26f /*{e-bhover-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #fcf0b5 /*{e-bhover-background-start}*/, #fbe26f /*{e-bhover-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #fcf0b5 /*{e-bhover-background-start}*/, #fbe26f /*{e-bhover-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #fcf0b5 /*{e-bhover-background-start}*/, #fbe26f /*{e-bhover-background-end}*/);
+}
+
+.ui-btn-hover-e a.ui-link-inherit {
+ color: #333 /*{e-bhover-color}*/;
+}
+.ui-btn-down-e {
+ border: 1px solid #F7C942 /*{e-bdown-border}*/;
+ background: #fceda7 /*{e-bdown-background-color}*/;
+ font-weight: bold;
+ color: #111 /*{e-bdown-color}*/;
+ text-shadow: 0 /*{e-bdown-shadow-x}*/ 1px /*{e-bdown-shadow-y}*/ 1px /*{e-bdown-shadow-radius}*/ #ffffff /*{e-bdown-shadow-color}*/;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fadb4e /*{e-bdown-background-start}*/), to(#fceda7 /*{e-bdown-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #fadb4e /*{e-bdown-background-start}*/, #fceda7 /*{e-bdown-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #fadb4e /*{e-bdown-background-start}*/, #fceda7 /*{e-bdown-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #fadb4e /*{e-bdown-background-start}*/, #fceda7 /*{e-bdown-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #fadb4e /*{e-bdown-background-start}*/, #fceda7 /*{e-bdown-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #fadb4e /*{e-bdown-background-start}*/, #fceda7 /*{e-bdown-background-end}*/);
+}
+.ui-btn-down-e a.ui-link-inherit {
+ color: #333 /*{e-bdown-color}*/;
+}
+.ui-btn-up-e,
+.ui-btn-hover-e,
+.ui-btn-down-e {
+ font-family: Helvetica, Arial, sans-serif /*{e-button-font}*/;
+ text-decoration: none;
+}
+
+/* Structure */
+
+/* links within "buttons"
+-----------------------------------------------------------------------------------------------------------*/
+
+a.ui-link-inherit {
+ text-decoration: none !important;
+}
+
+/* links and their different states
+-----------------------------------------------------------------------------------------------------------*/
+
+.ui-link{
+ color: #2489CE /*{global-link-color}*/
+}
+
+.ui-link:hover{
+ color: #2489CE /*{global-link-hover}*/
+}
+
+.ui-link:active{
+ color: #2489CE /*{global-link-active}*/
+}
+
+.ui-link:visited{
+ color: #2489CE /*{global-link-visited}*/
+}
+
+/* Active class used as the "on" state across all themes
+-----------------------------------------------------------------------------------------------------------*/
+
+.ui-btn-active {
+ border: 1px solid #155678 /*{global-active-border}*/;
+ background: #4596ce /*{global-active-background-color}*/;
+ font-weight: bold;
+ color: #fff /*{global-active-color}*/;
+ cursor: pointer;
+ text-shadow: 0 /*{global-active-shadow-x}*/ -1px /*{global-active-shadow-y}*/ 1px /*{global-active-shadow-radius}*/ #145072 /*{global-active-shadow-color}*/;
+ text-decoration: none;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#85bae4 /*{global-active-background-start}*/), to(#5393c5 /*{global-active-background-end}*/)); /* Saf4+, Chrome */
+ background-image: -webkit-linear-gradient(top, #85bae4 /*{global-active-background-start}*/, #5393c5 /*{global-active-background-end}*/); /* Chrome 10+, Saf5.1+ */
+ background-image: -moz-linear-gradient(top, #85bae4 /*{global-active-background-start}*/, #5393c5 /*{global-active-background-end}*/); /* FF3.6 */
+ background-image: -ms-linear-gradient(top, #85bae4 /*{global-active-background-start}*/, #5393c5 /*{global-active-background-end}*/); /* IE10 */
+ background-image: -o-linear-gradient(top, #85bae4 /*{global-active-background-start}*/, #5393c5 /*{global-active-background-end}*/); /* Opera 11.10+ */
+ background-image: linear-gradient(top, #85bae4 /*{global-active-background-start}*/, #5393c5 /*{global-active-background-end}*/);
+ outline: none;
+ font-family: Helvetica, Arial, sans-serif /*{global-active-font}*/;
+}
+.ui-btn-active a.ui-link-inherit {
+ color: #fff /*{global-active-color}*/;
+}
+
+
+/* button inner top highlight
+-----------------------------------------------------------------------------------------------------------*/
+
+.ui-btn-inner {
+ border-top: 1px solid #fff;
+ border-color: rgba(255,255,255,.3);
+}
+
+
+/* corner rounding classes
+-----------------------------------------------------------------------------------------------------------*/
+
+.ui-corner-tl {
+ -moz-border-radius-topleft: .6em /*{global-radii-blocks}*/;
+ -webkit-border-top-left-radius: .6em /*{global-radii-blocks}*/;
+ border-top-left-radius: .6em /*{global-radii-blocks}*/;
+}
+.ui-corner-tr {
+ -moz-border-radius-topright: .6em /*{global-radii-blocks}*/;
+ -webkit-border-top-right-radius: .6em /*{global-radii-blocks}*/;
+ border-top-right-radius: .6em /*{global-radii-blocks}*/;
+}
+.ui-corner-bl {
+ -moz-border-radius-bottomleft: .6em /*{global-radii-blocks}*/;
+ -webkit-border-bottom-left-radius: .6em /*{global-radii-blocks}*/;
+ border-bottom-left-radius: .6em /*{global-radii-blocks}*/;
+}
+.ui-corner-br {
+ -moz-border-radius-bottomright: .6em /*{global-radii-blocks}*/;
+ -webkit-border-bottom-right-radius: .6em /*{global-radii-blocks}*/;
+ border-bottom-right-radius: .6em /*{global-radii-blocks}*/;
+}
+.ui-corner-top {
+ -moz-border-radius-topleft: .6em /*{global-radii-blocks}*/;
+ -webkit-border-top-left-radius: .6em /*{global-radii-blocks}*/;
+ border-top-left-radius: .6em /*{global-radii-blocks}*/;
+ -moz-border-radius-topright: .6em /*{global-radii-blocks}*/;
+ -webkit-border-top-right-radius: .6em /*{global-radii-blocks}*/;
+ border-top-right-radius: .6em /*{global-radii-blocks}*/;
+}
+.ui-corner-bottom {
+ -moz-border-radius-bottomleft: .6em /*{global-radii-blocks}*/;
+ -webkit-border-bottom-left-radius: .6em /*{global-radii-blocks}*/;
+ border-bottom-left-radius: .6em /*{global-radii-blocks}*/;
+ -moz-border-radius-bottomright: .6em /*{global-radii-blocks}*/;
+ -webkit-border-bottom-right-radius: .6em /*{global-radii-blocks}*/;
+ border-bottom-right-radius: .6em /*{global-radii-blocks}*/;
+ }
+.ui-corner-right {
+ -moz-border-radius-topright: .6em /*{global-radii-blocks}*/;
+ -webkit-border-top-right-radius: .6em /*{global-radii-blocks}*/;
+ border-top-right-radius: .6em /*{global-radii-blocks}*/;
+ -moz-border-radius-bottomright: .6em /*{global-radii-blocks}*/;
+ -webkit-border-bottom-right-radius: .6em /*{global-radii-blocks}*/;
+ border-bottom-right-radius: .6em /*{global-radii-blocks}*/;
+}
+.ui-corner-left {
+ -moz-border-radius-topleft: .6em /*{global-radii-blocks}*/;
+ -webkit-border-top-left-radius: .6em /*{global-radii-blocks}*/;
+ border-top-left-radius: .6em /*{global-radii-blocks}*/;
+ -moz-border-radius-bottomleft: .6em /*{global-radii-blocks}*/;
+ -webkit-border-bottom-left-radius: .6em /*{global-radii-blocks}*/;
+ border-bottom-left-radius: .6em /*{global-radii-blocks}*/;
+}
+.ui-corner-all {
+ -moz-border-radius: .6em /*{global-radii-blocks}*/;
+ -webkit-border-radius: .6em /*{global-radii-blocks}*/;
+ border-radius: .6em /*{global-radii-blocks}*/;
+}
+.ui-corner-none {
+ -moz-border-radius: 0;
+ -webkit-border-radius: 0;
+ border-radius: 0;
+}
+
+/* Interaction cues
+-----------------------------------------------------------------------------------------------------------*/
+.ui-disabled {
+ opacity: .3;
+}
+.ui-disabled,
+.ui-disabled a {
+ cursor: default;
+}
+
+/* Icons
+-----------------------------------------------------------------------------------------------------------*/
+
+.ui-icon,
+.ui-icon-searchfield:after {
+ background: #666 /*{global-icon-color}*/;
+ background: rgba(0,0,0,.4) /*{global-icon-disc}*/;
+ background-image: url(images/icons-18-white.png) /*{global-icon-set}*/;
+ background-repeat: no-repeat;
+ -moz-border-radius: 9px;
+ -webkit-border-radius: 9px;
+ border-radius: 9px;
+}
+
+
+/* Alt icon color
+-----------------------------------------------------------------------------------------------------------*/
+
+.ui-icon-alt {
+ background: #fff;
+ background: rgba(255,255,255,.3);
+ background-image: url(images/icons-18-black.png);
+ background-repeat: no-repeat;
+}
+
+/* HD/"retina" sprite
+-----------------------------------------------------------------------------------------------------------*/
+
+@media only screen and (-webkit-min-device-pixel-ratio: 1.5),
+ only screen and (min--moz-device-pixel-ratio: 1.5),
+ only screen and (min-resolution: 240dpi) {
+
+ .ui-icon-plus, .ui-icon-minus, .ui-icon-delete, .ui-icon-arrow-r,
+ .ui-icon-arrow-l, .ui-icon-arrow-u, .ui-icon-arrow-d, .ui-icon-check,
+ .ui-icon-gear, .ui-icon-refresh, .ui-icon-forward, .ui-icon-back,
+ .ui-icon-grid, .ui-icon-star, .ui-icon-alert, .ui-icon-info, .ui-icon-home, .ui-icon-search, .ui-icon-searchfield:after,
+ .ui-icon-checkbox-off, .ui-icon-checkbox-on, .ui-icon-radio-off, .ui-icon-radio-on {
+ background-image: url(images/icons-36-white.png);
+ -moz-background-size: 776px 18px;
+ -o-background-size: 776px 18px;
+ -webkit-background-size: 776px 18px;
+ background-size: 776px 18px;
+ }
+ .ui-icon-alt {
+ background-image: url(images/icons-36-black.png);
+ }
+}
+
+/* plus minus */
+.ui-icon-plus {
+ background-position: -0 50%;
+}
+.ui-icon-minus {
+ background-position: -36px 50%;
+}
+
+/* delete/close */
+.ui-icon-delete {
+ background-position: -72px 50%;
+}
+
+/* arrows */
+.ui-icon-arrow-r {
+ background-position: -108px 50%;
+}
+.ui-icon-arrow-l {
+ background-position: -144px 50%;
+}
+.ui-icon-arrow-u {
+ background-position: -180px 50%;
+}
+.ui-icon-arrow-d {
+ background-position: -216px 50%;
+}
+
+/* misc */
+.ui-icon-check {
+ background-position: -252px 50%;
+}
+.ui-icon-gear {
+ background-position: -288px 50%;
+}
+.ui-icon-refresh {
+ background-position: -324px 50%;
+}
+.ui-icon-forward {
+ background-position: -360px 50%;
+}
+.ui-icon-back {
+ background-position: -396px 50%;
+}
+.ui-icon-grid {
+ background-position: -432px 50%;
+}
+.ui-icon-star {
+ background-position: -468px 50%;
+}
+.ui-icon-alert {
+ background-position: -504px 50%;
+}
+.ui-icon-info {
+ background-position: -540px 50%;
+}
+.ui-icon-home {
+ background-position: -576px 50%;
+}
+.ui-icon-search,
+.ui-icon-searchfield:after {
+ background-position: -612px 50%;
+}
+.ui-icon-checkbox-off {
+ background-position: -684px 50%;
+}
+.ui-icon-checkbox-on {
+ background-position: -648px 50%;
+}
+.ui-icon-radio-off {
+ background-position: -756px 50%;
+}
+.ui-icon-radio-on {
+ background-position: -720px 50%;
+}
+
+
+/* checks,radios */
+.ui-checkbox .ui-icon {
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+}
+.ui-icon-checkbox-off,
+.ui-icon-radio-off {
+ background-color: transparent;
+}
+.ui-checkbox-on .ui-icon,
+.ui-radio-on .ui-icon {
+ background-color: #4596ce /*{global-active-background-color}*/; /* NOTE: this hex should match the active state color. It's repeated here for cascade */
+}
+
+/* loading icon */
+.ui-icon-loading {
+ background-image: url(images/ajax-loader.png);
+ width: 40px;
+ height: 40px;
+ -moz-border-radius: 20px;
+ -webkit-border-radius: 20px;
+ border-radius: 20px;
+ background-size: 35px 35px;
+}
+
+
+/* Button corner classes
+-----------------------------------------------------------------------------------------------------------*/
+
+.ui-btn-corner-tl {
+ -moz-border-radius-topleft: 1em /*{global-radii-buttons}*/;
+ -webkit-border-top-left-radius: 1em /*{global-radii-buttons}*/;
+ border-top-left-radius: 1em /*{global-radii-buttons}*/;
+}
+.ui-btn-corner-tr {
+ -moz-border-radius-topright: 1em /*{global-radii-buttons}*/;
+ -webkit-border-top-right-radius: 1em /*{global-radii-buttons}*/;
+ border-top-right-radius: 1em /*{global-radii-buttons}*/;
+}
+.ui-btn-corner-bl {
+ -moz-border-radius-bottomleft: 1em /*{global-radii-buttons}*/;
+ -webkit-border-bottom-left-radius: 1em /*{global-radii-buttons}*/;
+ border-bottom-left-radius: 1em /*{global-radii-buttons}*/;
+}
+.ui-btn-corner-br {
+ -moz-border-radius-bottomright: 1em /*{global-radii-buttons}*/;
+ -webkit-border-bottom-right-radius: 1em /*{global-radii-buttons}*/;
+ border-bottom-right-radius: 1em /*{global-radii-buttons}*/;
+}
+.ui-btn-corner-top {
+ -moz-border-radius-topleft: 1em /*{global-radii-buttons}*/;
+ -webkit-border-top-left-radius: 1em /*{global-radii-buttons}*/;
+ border-top-left-radius: 1em /*{global-radii-buttons}*/;
+ -moz-border-radius-topright: 1em /*{global-radii-buttons}*/;
+ -webkit-border-top-right-radius: 1em /*{global-radii-buttons}*/;
+ border-top-right-radius: 1em /*{global-radii-buttons}*/;
+}
+.ui-btn-corner-bottom {
+ -moz-border-radius-bottomleft: 1em /*{global-radii-buttons}*/;
+ -webkit-border-bottom-left-radius: 1em /*{global-radii-buttons}*/;
+ border-bottom-left-radius: 1em /*{global-radii-buttons}*/;
+ -moz-border-radius-bottomright: 1em /*{global-radii-buttons}*/;
+ -webkit-border-bottom-right-radius: 1em /*{global-radii-buttons}*/;
+ border-bottom-right-radius: 1em /*{global-radii-buttons}*/;
+}
+.ui-btn-corner-right {
+ -moz-border-radius-topright: 1em /*{global-radii-buttons}*/;
+ -webkit-border-top-right-radius: 1em /*{global-radii-buttons}*/;
+ border-top-right-radius: 1em /*{global-radii-buttons}*/;
+ -moz-border-radius-bottomright: 1em /*{global-radii-buttons}*/;
+ -webkit-border-bottom-right-radius: 1em /*{global-radii-buttons}*/;
+ border-bottom-right-radius: 1em /*{global-radii-buttons}*/;
+}
+.ui-btn-corner-left {
+ -moz-border-radius-topleft: 1em /*{global-radii-buttons}*/;
+ -webkit-border-top-left-radius: 1em /*{global-radii-buttons}*/;
+ border-top-left-radius: 1em /*{global-radii-buttons}*/;
+ -moz-border-radius-bottomleft: 1em /*{global-radii-buttons}*/;
+ -webkit-border-bottom-left-radius: 1em /*{global-radii-buttons}*/;
+ border-bottom-left-radius: 1em /*{global-radii-buttons}*/;
+}
+.ui-btn-corner-all {
+ -moz-border-radius: 1em /*{global-radii-buttons}*/;
+ -webkit-border-radius: 1em /*{global-radii-buttons}*/;
+ border-radius: 1em /*{global-radii-buttons}*/;
+}
+
+/* radius clip workaround for cleaning up corner trapping */
+.ui-corner-tl,
+.ui-corner-tr,
+.ui-corner-bl,
+.ui-corner-br,
+.ui-corner-top,
+.ui-corner-bottom,
+.ui-corner-right,
+.ui-corner-left,
+.ui-corner-all,
+.ui-btn-corner-tl,
+.ui-btn-corner-tr,
+.ui-btn-corner-bl,
+.ui-btn-corner-br,
+.ui-btn-corner-top,
+.ui-btn-corner-bottom,
+.ui-btn-corner-right,
+.ui-btn-corner-left,
+.ui-btn-corner-all {
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding;
+ background-clip: padding-box;
+}
+
+/* Overlay / modal
+-----------------------------------------------------------------------------------------------------------*/
+
+.ui-overlay {
+ background: #666;
+ opacity: .5;
+ filter: Alpha(Opacity=50);
+ position: absolute;
+ width: 100%;
+ height: 100%;
+}
+.ui-overlay-shadow {
+ -moz-box-shadow: 0px 0px 12px rgba(0,0,0,.6);
+ -webkit-box-shadow: 0px 0px 12px rgba(0,0,0,.6);
+ box-shadow: 0px 0px 12px rgba(0,0,0,.6);
+}
+.ui-shadow {
+ -moz-box-shadow: 0px 1px 4px /*{global-box-shadow-size}*/ rgba(0,0,0,.3) /*{global-box-shadow-color}*/;
+ -webkit-box-shadow: 0px 1px 4px /*{global-box-shadow-size}*/ rgba(0,0,0,.3) /*{global-box-shadow-color}*/;
+ box-shadow: 0px 1px 4px /*{global-box-shadow-size}*/ rgba(0,0,0,.3) /*{global-box-shadow-color}*/;
+}
+.ui-bar-a .ui-shadow,
+.ui-bar-b .ui-shadow ,
+.ui-bar-c .ui-shadow {
+ -moz-box-shadow: 0px 1px 0 rgba(255,255,255,.3);
+ -webkit-box-shadow: 0px 1px 0 rgba(255,255,255,.3);
+ box-shadow: 0px 1px 0 rgba(255,255,255,.3);
+}
+.ui-shadow-inset {
+ -moz-box-shadow: inset 0px 1px 4px rgba(0,0,0,.2);
+ -webkit-box-shadow: inset 0px 1px 4px rgba(0,0,0,.2);
+ box-shadow: inset 0px 1px 4px rgba(0,0,0,.2);
+}
+.ui-icon-shadow {
+ -moz-box-shadow: 0px 1px 0 rgba(255,255,255,.4);
+ -webkit-box-shadow: 0px 1px 0 rgba(255,255,255,.4);
+ box-shadow: 0px 1px 0 rgba(255,255,255,.4);
+}
+
+/* Focus state - set here for specificity
+-----------------------------------------------------------------------------------------------------------*/
+
+.ui-focus {
+ -moz-box-shadow: 0px 0px 12px #387bbe /*{global-active-background-color}*/;
+ -webkit-box-shadow: 0px 0px 12px #387bbe /*{global-active-background-color}*/;
+ box-shadow: 0px 0px 12px #387bbe /*{global-active-background-color}*/;
+}
+
+/* unset box shadow in browsers that don't do it right
+-----------------------------------------------------------------------------------------------------------*/
+
+.ui-mobile-nosupport-boxshadow * {
+ -moz-box-shadow: none !important;
+ -webkit-box-shadow: none !important;
+ box-shadow: none !important;
+}
+
+/* ...and bring back focus */
+.ui-mobile-nosupport-boxshadow .ui-focus {
+ outline-width: 2px;
+}/*
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+*/
+
+/* some unsets - more probably needed */
+.ui-mobile, .ui-mobile body { height: 100%; }
+.ui-mobile fieldset, .ui-page { padding: 0; margin: 0; }
+.ui-mobile a img, .ui-mobile fieldset { border: 0; }
+
+/* responsive page widths */
+.ui-mobile-viewport { margin: 0; overflow-x: hidden; -webkit-text-size-adjust: none; -ms-text-size-adjust:none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); }
+
+/* "page" containers - full-screen views, one should always be in view post-pageload */
+.ui-mobile [data-role=page], .ui-mobile [data-role=dialog], .ui-page { top: 0; left: 0; width: 100%; min-height: 100%; position: absolute; display: none; border: 0; }
+.ui-mobile .ui-page-active { display: block; overflow: visible; }
+
+/* on ios4, setting focus on the page element causes flashing during transitions when there is an outline, so we turn off outlines */
+.ui-page { outline: none; }
+
+/* native overflow scrolling */
+.ui-page.ui-mobile-touch-overflow,
+.ui-mobile-touch-overflow.ui-native-fixed .ui-content {
+ overflow: auto;
+ height: 100%;
+ -webkit-overflow-scrolling: touch;
+ -moz-overflow-scrolling: touch;
+ -o-overflow-scrolling: touch;
+ -ms-overflow-scrolling: touch;
+ overflow-scrolling: touch;
+}
+.ui-page.ui-mobile-touch-overflow,
+.ui-page.ui-mobile-touch-overflow * {
+ /* some level of transform keeps elements from blinking out of visibility on iOS */
+ -webkit-transform: rotateY(0);
+}
+.ui-page.ui-mobile-pre-transition {
+ display: block;
+}
+
+/* loading screen */
+.ui-loading .ui-mobile-viewport { overflow: hidden !important; }
+.ui-loading .ui-loader { display: block; }
+.ui-loading .ui-page { overflow: hidden; }
+.ui-loader { display: none; position: absolute; opacity: .85; z-index: 100; left: 50%; width: 200px; margin-left: -130px; margin-top: -35px; padding: 10px 30px; }
+.ui-loader h1 { font-size: 15px; text-align: center; }
+.ui-loader .ui-icon { position: static; display: block; opacity: .9; margin: 0 auto; width: 35px; height: 35px; background-color: transparent; }
+
+/*fouc*/
+.ui-mobile-rendering > * { visibility: hidden; }
+
+/*headers, content panels*/
+.ui-bar, .ui-body { position: relative; padding: .4em 15px; overflow: hidden; display: block; clear:both; }
+.ui-bar { font-size: 16px; margin: 0; }
+.ui-bar h1, .ui-bar h2, .ui-bar h3, .ui-bar h4, .ui-bar h5, .ui-bar h6 { margin: 0; padding: 0; font-size: 16px; display: inline-block; }
+
+.ui-header, .ui-footer { display: block; }
+.ui-page .ui-header, .ui-page .ui-footer { position: relative; }
+.ui-header .ui-btn-left { position: absolute; left: 10px; top: .4em; }
+.ui-header .ui-btn-right { position: absolute; right: 10px; top: .4em; }
+.ui-header .ui-title, .ui-footer .ui-title { min-height: 1.1em; text-align: center; font-size: 16px; display: block; margin: .6em 90px .8em; padding: 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; outline: 0 !important; }
+
+/*content area*/
+.ui-content { border-width: 0; overflow: visible; overflow-x: hidden; padding: 15px; }
+.ui-page-fullscreen .ui-content { padding:0; }
+
+/* native fixed headers and footers */
+.ui-mobile-touch-overflow.ui-page.ui-native-fixed,
+.ui-mobile-touch-overflow.ui-page.ui-native-fullscreen {
+ overflow: visible;
+}
+.ui-mobile-touch-overflow.ui-native-fixed .ui-header,
+.ui-mobile-touch-overflow.ui-native-fixed .ui-footer {
+ position: fixed;
+ left: 0;
+ right: 0;
+ top: 0;
+ z-index: 200;
+}
+.ui-mobile-touch-overflow.ui-page.ui-native-fixed .ui-footer {
+ top: auto;
+ bottom: 0;
+}
+.ui-mobile-touch-overflow.ui-native-fixed .ui-content {
+ padding-top: 2.5em;
+ padding-bottom: 3em;
+ top: 0;
+ bottom: 0;
+ height: auto;
+ position: absolute;
+}
+.ui-mobile-touch-overflow.ui-native-fullscreen .ui-content {
+ padding-top: 0;
+ padding-bottom: 0;
+}
+.ui-mobile-touch-overflow.ui-native-fullscreen .ui-header,
+.ui-mobile-touch-overflow.ui-native-fullscreen .ui-footer {
+ opacity: .9;
+}
+.ui-native-bars-hidden {
+ display: none;
+}
+
+/* icons sizing */
+.ui-icon { width: 18px; height: 18px; }
+
+/* fullscreen class on ui-content div */
+.ui-fullscreen { }
+.ui-fullscreen img { max-width: 100%; }
+
+/* non-js content hiding */
+.ui-nojs { position: absolute; left: -9999px; }
+/*
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
+*/
+.spin {
+ -webkit-transform: rotate(360deg);
+ -webkit-animation-name: spin;
+ -webkit-animation-duration: 1s;
+ -webkit-animation-iteration-count: infinite;
+ -webkit-animation-timing-function: linear;
+}
+@-webkit-keyframes spin {
+ from {-webkit-transform: rotate(0deg);}
+ to {-webkit-transform: rotate(360deg);}
+}
+
+/* Transitions from jQtouch (with small modifications): http://www.jqtouch.com/
+Built by David Kaneda and maintained by Jonathan Stark.
+*/
+.in, .out {
+ -webkit-animation-timing-function: ease-in-out;
+ -webkit-animation-duration: 350ms;
+}
+
+
+.slide.out {
+ -webkit-transform: translateX(-100%);
+ -webkit-animation-name: slideouttoleft;
+}
+
+.slide.in {
+ -webkit-transform: translateX(0);
+ -webkit-animation-name: slideinfromright;
+}
+
+.slide.out.reverse {
+ -webkit-transform: translateX(100%);
+ -webkit-animation-name: slideouttoright;
+}
+
+.slide.in.reverse {
+ -webkit-transform: translateX(0);
+ -webkit-animation-name: slideinfromleft;
+}
+
+.slideup.out {
+ -webkit-animation-name: dontmove;
+ z-index: 0;
+}
+
+.slideup.in {
+ -webkit-transform: translateY(0);
+ -webkit-animation-name: slideinfrombottom;
+ z-index: 10;
+}
+
+.slideup.in.reverse {
+ z-index: 0;
+ -webkit-animation-name: dontmove;
+}
+
+.slideup.out.reverse {
+ -webkit-transform: translateY(100%);
+ z-index: 10;
+ -webkit-animation-name: slideouttobottom;
+}
+
+.slidedown.out {
+ -webkit-animation-name: dontmove;
+ z-index: 0;
+}
+
+.slidedown.in {
+ -webkit-transform: translateY(0);
+ -webkit-animation-name: slideinfromtop;
+ z-index: 10;
+}
+
+.slidedown.in.reverse {
+ z-index: 0;
+ -webkit-animation-name: dontmove;
+}
+
+.slidedown.out.reverse {
+ -webkit-transform: translateY(-100%);
+ z-index: 10;
+ -webkit-animation-name: slideouttotop;
+}
+
+@-webkit-keyframes slideinfromright {
+ from { -webkit-transform: translateX(100%); }
+ to { -webkit-transform: translateX(0); }
+}
+
+@-webkit-keyframes slideinfromleft {
+ from { -webkit-transform: translateX(-100%); }
+ to { -webkit-transform: translateX(0); }
+}
+
+@-webkit-keyframes slideouttoleft {
+ from { -webkit-transform: translateX(0); }
+ to { -webkit-transform: translateX(-100%); }
+}
+
+@-webkit-keyframes slideouttoright {
+ from { -webkit-transform: translateX(0); }
+ to { -webkit-transform: translateX(100%); }
+}
+
+@-webkit-keyframes slideinfromtop {
+ from { -webkit-transform: translateY(-100%); }
+ to { -webkit-transform: translateY(0); }
+}
+
+@-webkit-keyframes slideinfrombottom {
+ from { -webkit-transform: translateY(100%); }
+ to { -webkit-transform: translateY(0); }
+}
+
+@-webkit-keyframes slideouttobottom {
+ from { -webkit-transform: translateY(0); }
+ to { -webkit-transform: translateY(100%); }
+}
+
+@-webkit-keyframes slideouttotop {
+ from { -webkit-transform: translateY(0); }
+ to { -webkit-transform: translateY(-100%); }
+}
+@-webkit-keyframes fadein {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+
+@-webkit-keyframes fadeout {
+ from { opacity: 1; }
+ to { opacity: 0; }
+}
+
+.fade.out {
+ z-index: 0;
+ -webkit-animation-name: fadeout;
+}
+
+.fade.in {
+ opacity: 1;
+ z-index: 10;
+ -webkit-animation-name: fadein;
+}
+
+/* The properties in this rule are only necessary for the 'flip' transition.
+ * We need specify the perspective to create a projection matrix. This will add
+ * some depth as the element flips. The depth number represents the distance of
+ * the viewer from the z-plane. According to the CSS3 spec, 1000 is a moderate
+ * value.
+ */
+.viewport-flip {
+ -webkit-perspective: 1000;
+ position: absolute;
+}
+
+.ui-mobile-viewport-transitioning,
+.ui-mobile-viewport-transitioning .ui-page {
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+}
+
+.flip {
+ -webkit-animation-duration: .65s;
+ -webkit-backface-visibility:hidden;
+ -webkit-transform:translateX(0); /* Needed to work around an iOS 3.1 bug that causes listview thumbs to disappear when -webkit-visibility:hidden is used. */
+}
+
+.flip.out {
+ -webkit-transform: rotateY(-180deg) scale(.8);
+ -webkit-animation-name: flipouttoleft;
+}
+
+.flip.in {
+ -webkit-transform: rotateY(0) scale(1);
+ -webkit-animation-name: flipinfromleft;
+}
+
+/* Shake it all about */
+
+.flip.out.reverse {
+ -webkit-transform: rotateY(180deg) scale(.8);
+ -webkit-animation-name: flipouttoright;
+}
+
+.flip.in.reverse {
+ -webkit-transform: rotateY(0) scale(1);
+ -webkit-animation-name: flipinfromright;
+}
+
+@-webkit-keyframes flipinfromright {
+ from { -webkit-transform: rotateY(-180deg) scale(.8); }
+ to { -webkit-transform: rotateY(0) scale(1); }
+}
+
+@-webkit-keyframes flipinfromleft {
+ from { -webkit-transform: rotateY(180deg) scale(.8); }
+ to { -webkit-transform: rotateY(0) scale(1); }
+}
+
+@-webkit-keyframes flipouttoleft {
+ from { -webkit-transform: rotateY(0) scale(1); }
+ to { -webkit-transform: rotateY(-180deg) scale(.8); }
+}
+
+@-webkit-keyframes flipouttoright {
+ from { -webkit-transform: rotateY(0) scale(1); }
+ to { -webkit-transform: rotateY(180deg) scale(.8); }
+}
+
+
+/* Hackish, but reliable. */
+
+@-webkit-keyframes dontmove {
+ from { opacity: 1; }
+ to { opacity: 1; }
+}
+
+.pop {
+ -webkit-transform-origin: 50% 50%;
+}
+
+.pop.in {
+ -webkit-transform: scale(1);
+ opacity: 1;
+ -webkit-animation-name: popin;
+ z-index: 10;
+}
+
+.pop.in.reverse {
+ z-index: 0;
+ -webkit-animation-name: dontmove;
+}
+
+.pop.out.reverse {
+ -webkit-transform: scale(.2);
+ opacity: 0;
+ -webkit-animation-name: popout;
+ z-index: 10;
+}
+
+@-webkit-keyframes popin {
+ from {
+ -webkit-transform: scale(.2);
+ opacity: 0;
+ }
+ to {
+ -webkit-transform: scale(1);
+ opacity: 1;
+ }
+}
+
+@-webkit-keyframes popout {
+ from {
+ -webkit-transform: scale(1);
+ opacity: 1;
+ }
+ to {
+ -webkit-transform: scale(.2);
+ opacity: 0;
+ }
+}/*
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
+*/
+
+/* content configurations. */
+.ui-grid-a, .ui-grid-b, .ui-grid-c, .ui-grid-d { overflow: hidden; }
+.ui-block-a, .ui-block-b, .ui-block-c, .ui-block-d, .ui-block-e { margin: 0; padding: 0; border: 0; float: left; min-height:1px;}
+
+/* grid solo: 100 - single item fallback */
+.ui-grid-solo .ui-block-a { width: 100%; float: none; }
+
+/* grid a: 50/50 */
+.ui-grid-a .ui-block-a, .ui-grid-a .ui-block-b { width: 50%; }
+.ui-grid-a .ui-block-a { clear: left; }
+
+/* grid b: 33/33/33 */
+.ui-grid-b .ui-block-a, .ui-grid-b .ui-block-b, .ui-grid-b .ui-block-c { width: 33.333%; }
+.ui-grid-b .ui-block-a { clear: left; }
+
+/* grid c: 25/25/25/25 */
+.ui-grid-c .ui-block-a, .ui-grid-c .ui-block-b, .ui-grid-c .ui-block-c, .ui-grid-c .ui-block-d { width: 25%; }
+.ui-grid-c .ui-block-a { clear: left; }
+
+/* grid d: 20/20/20/20/20 */
+.ui-grid-d .ui-block-a, .ui-grid-d .ui-block-b, .ui-grid-d .ui-block-c, .ui-grid-d .ui-block-d, .ui-grid-d .ui-block-e { width: 20%; }
+.ui-grid-d .ui-block-a { clear: left; }
+/*
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
+*/
+/* fixed page header & footer configuration */
+.ui-header, .ui-footer, .ui-page-fullscreen .ui-header, .ui-page-fullscreen .ui-footer { position: absolute; overflow: hidden; width: 100%; border-left-width: 0; border-right-width: 0; }
+.ui-header-fixed, .ui-footer-fixed {
+ z-index: 1000;
+ -webkit-transform: translateZ(0); /* Force header/footer rendering to go through the same rendering pipeline as native page scrolling. */
+}
+.ui-footer-duplicate, .ui-page-fullscreen .ui-fixed-inline { display: none; }
+.ui-page-fullscreen .ui-header, .ui-page-fullscreen .ui-footer { opacity: .9; }
+/*
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
+*/
+.ui-navbar { overflow: hidden; }
+.ui-navbar ul, .ui-navbar-expanded ul { list-style:none; padding: 0; margin: 0; position: relative; display: block; border: 0;}
+.ui-navbar-collapsed ul { float: left; width: 75%; margin-right: -2px; }
+.ui-navbar-collapsed .ui-navbar-toggle { float: left; width: 25%; }
+.ui-navbar li.ui-navbar-truncate { position: absolute; left: -9999px; top: -9999px; }
+.ui-navbar li .ui-btn, .ui-navbar .ui-navbar-toggle .ui-btn { display: block; font-size: 12px; text-align: center; margin: 0; border-right-width: 0; }
+.ui-navbar li .ui-btn { margin-right: -1px; }
+.ui-navbar li .ui-btn:last-child { margin-right: 0; }
+.ui-header .ui-navbar li .ui-btn, .ui-header .ui-navbar .ui-navbar-toggle .ui-btn,
+.ui-footer .ui-navbar li .ui-btn, .ui-footer .ui-navbar .ui-navbar-toggle .ui-btn { border-top-width: 0; border-bottom-width: 0; }
+.ui-navbar .ui-btn-inner { padding-left: 2px; padding-right: 2px; }
+.ui-navbar-noicons li .ui-btn .ui-btn-inner, .ui-navbar-noicons .ui-navbar-toggle .ui-btn-inner { padding-top: .8em; padding-bottom: .9em; }
+/*expanded page styles*/
+.ui-navbar-expanded .ui-btn { margin: 0; font-size: 14px; }
+.ui-navbar-expanded .ui-btn-inner { padding-left: 5px; padding-right: 5px; }
+.ui-navbar-expanded .ui-btn-icon-top .ui-btn-inner { padding: 45px 5px 15px; text-align: center; }
+.ui-navbar-expanded .ui-btn-icon-top .ui-icon { top: 15px; }
+.ui-navbar-expanded .ui-btn-icon-bottom .ui-btn-inner { padding: 15px 5px 45px; text-align: center; }
+.ui-navbar-expanded .ui-btn-icon-bottom .ui-icon { bottom: 15px; }
+.ui-navbar-expanded li .ui-btn .ui-btn-inner { min-height: 2.5em; }
+.ui-navbar-expanded .ui-navbar-noicons .ui-btn .ui-btn-inner { padding-top: 1.8em; padding-bottom: 1.9em; }
+/*
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
+*/
+.ui-btn { display: block; text-align: center; cursor:pointer; position: relative; margin: .5em 5px; padding: 0; }
+.ui-btn:focus, .ui-btn:active { outline: none; }
+.ui-header .ui-btn, .ui-footer .ui-btn, .ui-bar .ui-btn { display: inline-block; font-size: 13px; margin: 0; }
+.ui-btn-inline { display: inline-block; }
+.ui-btn-inner { padding: .6em 25px; display: block; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; position: relative; zoom: 1; }
+.ui-header .ui-btn-inner, .ui-footer .ui-btn-inner, .ui-bar .ui-btn-inner { padding: .4em 8px .5em; }
+.ui-btn-icon-notext { width: 24px; height: 24px; }
+.ui-btn-icon-notext .ui-btn-inner { padding: 2px 1px 2px 3px; }
+.ui-btn-icon-notext .ui-btn-text { position: absolute; left: -999px; }
+.ui-btn-icon-left .ui-btn-inner { padding-left: 33px; }
+.ui-header .ui-btn-icon-left .ui-btn-inner,
+.ui-footer .ui-btn-icon-left .ui-btn-inner,
+.ui-bar .ui-btn-icon-left .ui-btn-inner { padding-left: 27px; }
+.ui-btn-icon-right .ui-btn-inner { padding-right: 33px; }
+.ui-header .ui-btn-icon-right .ui-btn-inner,
+.ui-footer .ui-btn-icon-right .ui-btn-inner,
+.ui-bar .ui-btn-icon-right .ui-btn-inner { padding-right: 27px; }
+.ui-btn-icon-top .ui-btn-inner { padding-top: 33px; }
+.ui-header .ui-btn-icon-top .ui-btn-inner,
+.ui-footer .ui-btn-icon-top .ui-btn-inner,
+.ui-bar .ui-btn-icon-top .ui-btn-inner { padding-top: 27px; }
+.ui-btn-icon-bottom .ui-btn-inner { padding-bottom: 33px; }
+.ui-header .ui-btn-icon-bottom .ui-btn-inner,
+.ui-footer .ui-btn-icon-bottom .ui-btn-inner,
+.ui-bar .ui-btn-icon-bottom .ui-btn-inner { padding-bottom: 27px; }
+
+/*btn icon positioning*/
+.ui-btn-icon-notext .ui-icon { display: block; }
+.ui-btn-icon-left .ui-icon, .ui-btn-icon-right .ui-icon { position: absolute; top: 50%; margin-top: -9px; }
+.ui-btn-icon-top .ui-icon, .ui-btn-icon-bottom .ui-icon { position: absolute; left: 50%; margin-left: -9px; }
+.ui-btn-icon-left .ui-icon { left: 10px; }
+.ui-btn-icon-right .ui-icon { right: 10px; }
+.ui-btn-icon-top .ui-icon { top: 10px; }
+.ui-btn-icon-bottom .ui-icon { bottom: 10px; }
+.ui-header .ui-btn-icon-left .ui-icon,
+.ui-footer .ui-btn-icon-left .ui-icon,
+.ui-bar .ui-btn-icon-left .ui-icon { left: 4px; }
+.ui-header .ui-btn-icon-right .ui-icon,
+.ui-footer .ui-btn-icon-right .ui-icon,
+.ui-bar .ui-btn-icon-right .ui-icon { right: 4px; }
+.ui-header .ui-btn-icon-top .ui-icon,
+.ui-footer .ui-btn-icon-top .ui-icon,
+.ui-bar .ui-btn-icon-top .ui-icon { top: 4px; }
+.ui-header .ui-btn-icon-bottom .ui-icon,
+.ui-footer .ui-btn-icon-bottom .ui-icon,
+.ui-bar .ui-btn-icon-bottom .ui-icon { bottom: 4px; }
+
+/*hiding native button,inputs */
+.ui-btn-hidden { position: absolute; top: 0; left: 0; width: 100%; height: 100%; -webkit-appearance: button; opacity: .1; cursor: pointer; background: transparent; font-size: 1px; border: none; line-height: 999px; }
+/*
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
+*/
+.ui-collapsible { margin: .5em 0; }
+.ui-collapsible-heading { font-size: 16px; display: block; margin: 0 -8px; padding: 0; border-width: 0 0 1px 0; position: relative; }
+.ui-collapsible-heading a { text-align: left; margin: 0; }
+.ui-collapsible-heading a .ui-btn-inner { padding-left: 40px; }
+.ui-collapsible-heading a span.ui-btn { position: absolute; left: 6px; top: 50%; margin: -12px 0 0 0; width: 20px; height: 20px; padding: 1px 0px 1px 2px; text-indent: -9999px; }
+.ui-collapsible-heading a span.ui-btn .ui-btn-inner { padding: 10px 0; }
+.ui-collapsible-heading a span.ui-btn .ui-icon { left: 0; margin-top: -10px; }
+.ui-collapsible-heading-status { position:absolute; left:-9999px; }
+.ui-collapsible-content {
+ display: block;
+ margin: 0 -8px;
+ padding: 10px 16px;
+ border-top: none; /* Overrides ui-btn-up-* */
+ background-image: none; /* Overrides ui-btn-up-* */
+ font-weight: normal; /* Overrides ui-btn-up-* */
+}
+.ui-collapsible-content-collapsed { display: none; }
+
+.ui-collapsible-set { margin: .5em 0; }
+.ui-collapsible-set .ui-collapsible { margin: -1px 0 0; }
+/*
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
+*/
+.ui-controlgroup, fieldset.ui-controlgroup { padding: 0; margin: .5em 0 1em; }
+.ui-bar .ui-controlgroup { margin: 0 .3em; }
+.ui-controlgroup-label { font-size: 16px; line-height: 1.4; font-weight: normal; margin: 0 0 .3em; }
+.ui-controlgroup-controls { display: block; width: 95%;}
+.ui-controlgroup li { list-style: none; }
+.ui-controlgroup-vertical .ui-btn,
+.ui-controlgroup-vertical .ui-checkbox, .ui-controlgroup-vertical .ui-radio { margin: 0; border-bottom-width: 0; }
+.ui-controlgroup-vertical .ui-controlgroup-last { border-bottom-width: 1px; }
+.ui-controlgroup-horizontal { padding: 0; }
+.ui-controlgroup-horizontal .ui-btn { display: inline-block; margin: 0 -5px 0 0; }
+.ui-controlgroup-horizontal .ui-checkbox, .ui-controlgroup-horizontal .ui-radio { float: left; margin: 0 -1px 0 0; }
+.ui-controlgroup-horizontal .ui-checkbox .ui-btn, .ui-controlgroup-horizontal .ui-radio .ui-btn,
+.ui-controlgroup-horizontal .ui-checkbox:last-child, .ui-controlgroup-horizontal .ui-radio:last-child { margin-right: 0; }
+.ui-controlgroup-horizontal .ui-controlgroup-last { margin-right: 0; }
+.ui-controlgroup .ui-checkbox label, .ui-controlgroup .ui-radio label { font-size: 16px; }
+/* conflicts with listview..
+.ui-controlgroup .ui-btn-icon-notext { width: 30px; height: 30px; text-indent: -9999px; }
+.ui-controlgroup .ui-btn-icon-notext .ui-btn-inner { padding: 5px 6px 5px 5px; }
+*/
+
+@media all and (min-width: 450px){
+ .ui-controlgroup-label { vertical-align: top; display: inline-block; width: 20%; margin: 0 2% 0 0; }
+ .ui-controlgroup-controls { width: 60%; display: inline-block; }
+} /*
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
+*/
+.ui-dialog { min-height: 480px; }
+.ui-dialog .ui-header, .ui-dialog .ui-content, .ui-dialog .ui-footer { margin: 15px; position: relative; }
+.ui-dialog .ui-header, .ui-dialog .ui-footer { z-index: 10; width: auto; }
+.ui-dialog .ui-content, .ui-dialog .ui-footer { margin-top: -15px; }/*
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
+*/
+.ui-checkbox, .ui-radio { position:relative; margin: .2em 0 .5em; z-index: 1; }
+.ui-checkbox .ui-btn, .ui-radio .ui-btn { margin: 0; text-align: left; z-index: 2; }
+.ui-checkbox .ui-btn-inner, .ui-radio .ui-btn-inner { white-space: normal; }
+.ui-checkbox .ui-btn-icon-left .ui-btn-inner,.ui-radio .ui-btn-icon-left .ui-btn-inner { padding-left: 45px; }
+.ui-checkbox .ui-btn-icon-right .ui-btn-inner, .ui-radio .ui-btn-icon-right .ui-btn-inner { padding-right: 45px; }
+.ui-checkbox .ui-icon, .ui-radio .ui-icon { top: 1.1em; }
+.ui-checkbox .ui-btn-icon-left .ui-icon, .ui-radio .ui-btn-icon-left .ui-icon {left: 15px; }
+.ui-checkbox .ui-btn-icon-right .ui-icon, .ui-radio .ui-btn-icon-right .ui-icon {right: 15px; }
+/* input, label positioning */
+.ui-checkbox input,.ui-radio input { position:absolute; left:20px; top:50%; width: 10px; height: 10px; margin:-5px 0 0 0; outline: 0 !important; z-index: 1; }/*
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
+*/
+.ui-field-contain { padding: 1.5em 0; margin: 0; border-bottom-width: 1px; overflow: visible; }
+.ui-field-contain:first-child { border-top-width: 0; }
+@media all and (min-width: 450px){
+ .ui-field-contain { border-width: 0; padding: 0; margin: 1em 0; }
+} /*
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
+*/
+.ui-select { display: block; position: relative; }
+.ui-select select { position: absolute; left: -9999px; top: -9999px; }
+.ui-select .ui-btn { overflow: hidden; }
+.ui-select .ui-btn select { cursor: pointer; -webkit-appearance: button; left: 0; top:0; width: 100%; min-height: 1.5em; min-height: 100%; height: 3em; max-height: 100%; opacity: 0; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter: alpha(opacity=0); z-index: 2; }
+@-moz-document url-prefix() {.ui-select .ui-btn select { opacity: 0.0001; }}
+.ui-select .ui-btn select.ui-select-nativeonly { opacity: 1; text-indent: 0; }
+
+.ui-select .ui-btn-icon-right .ui-btn-inner { padding-right: 45px; }
+.ui-select .ui-btn-icon-right .ui-icon { right: 15px; }
+
+/* labels */
+label.ui-select { font-size: 16px; line-height: 1.4; font-weight: normal; margin: 0 0 .3em; display: block; }
+
+/*listbox*/
+.ui-select .ui-btn-text, .ui-selectmenu .ui-btn-text { display: block; min-height: 1em; }
+.ui-select .ui-btn-text { text-overflow: ellipsis; overflow: hidden;}
+
+.ui-selectmenu { position: absolute; padding: 0; z-index: 100 !important; width: 80%; max-width: 350px; padding: 6px; }
+.ui-selectmenu .ui-listview { margin: 0; }
+.ui-selectmenu .ui-btn.ui-li-divider { cursor: default; }
+.ui-selectmenu-hidden { top: -9999px; left: -9999px; }
+.ui-selectmenu-screen { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 99; }
+.ui-screen-hidden, .ui-selectmenu-list .ui-li .ui-icon { display: none; }
+.ui-selectmenu-list .ui-li .ui-icon { display: block; }
+.ui-li.ui-selectmenu-placeholder { display: none; }
+.ui-selectmenu .ui-header .ui-title { margin: 0.6em 46px 0.8em; }
+
+@media all and (min-width: 450px){
+ label.ui-select { vertical-align: top; display: inline-block; width: 20%; margin: 0 2% 0 0; }
+ .ui-select { width: 60%; display: inline-block; }
+}
+
+/* when no placeholder is defined in a multiple select, the header height doesn't even extend past the close button. this shim's content in there */
+.ui-selectmenu .ui-header h1:after { content: '.'; visibility: hidden; }/*
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
+*/
+label.ui-input-text { font-size: 16px; line-height: 1.4; display: block; font-weight: normal; margin: 0 0 .3em; }
+input.ui-input-text, textarea.ui-input-text { background-image: none; padding: .4em; line-height: 1.4; font-size: 16px; display: block; width: 95%; }
+input.ui-input-text { -webkit-appearance: none; }
+textarea.ui-input-text { height: 50px; -webkit-transition: height 200ms linear; -moz-transition: height 200ms linear; -o-transition: height 200ms linear; transition: height 200ms linear; }
+.ui-input-search { padding: 0 30px; width: 77%; background-image: none; position: relative; }
+.ui-icon-searchfield:after { position: absolute; left: 7px; top: 50%; margin-top: -9px; content: ""; width: 18px; height: 18px; opacity: .5; }
+.ui-input-search input.ui-input-text { border: none; width: 98%; padding: .4em 0; margin: 0; display: block; background: transparent none; outline: 0 !important; }
+.ui-input-search .ui-input-clear { position: absolute; right: 0; top: 50%; margin-top: -14px; }
+.ui-input-search .ui-input-clear-hidden { display: none; }
+
+/* orientation adjustments - incomplete!*/
+@media all and (min-width: 450px){
+ label.ui-input-text { vertical-align: top; display: inline-block; width: 20%; margin: 0 2% 0 0 }
+ input.ui-input-text,
+ textarea.ui-input-text,
+ .ui-input-search { width: 60%; display: inline-block; }
+ .ui-input-search { width: 50%; }
+ .ui-input-search input.ui-input-text { width: 98%; /*echos rule from above*/ }
+}/*
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
+*/
+.ui-listview { margin: 0; counter-reset: listnumbering; }
+.ui-content .ui-listview { margin: -15px; }
+.ui-content .ui-listview-inset { margin: 1em 0; }
+.ui-listview, .ui-li { list-style:none; padding:0; }
+.ui-li, .ui-li.ui-field-contain { display: block; margin:0; position: relative; overflow: visible; text-align: left; border-width: 0; border-top-width: 1px; }
+.ui-li .ui-btn-text { position: relative; z-index: 1; }
+.ui-li .ui-btn-text a.ui-link-inherit { text-overflow: ellipsis; overflow: hidden; white-space: nowrap; }
+.ui-li-divider, .ui-li-static { padding: .5em 15px; font-size: 14px; font-weight: bold; }
+.ui-li-divider { counter-reset: listnumbering; }
+ol.ui-listview .ui-link-inherit:before, ol.ui-listview .ui-li-static:before, .ui-li-dec { font-size: .8em; display: inline-block; padding-right: .3em; font-weight: normal;counter-increment: listnumbering; content: counter(listnumbering) ". "; }
+ol.ui-listview .ui-li-jsnumbering:before { content: "" !important; } /* to avoid chance of duplication */
+.ui-listview-inset .ui-li { border-right-width: 1px; border-left-width: 1px; }
+.ui-li:last-child, .ui-li.ui-field-contain:last-child { border-bottom-width: 1px; }
+.ui-li>.ui-btn-inner { display: block; position: relative; padding: 0; }
+.ui-li .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li { padding: .7em 15px .7em 15px; display: block; }
+.ui-li-has-thumb .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-thumb { min-height: 60px; padding-left: 100px; }
+.ui-li-has-icon .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-icon { min-height: 20px; padding-left: 40px; }
+.ui-li-has-count .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-count { padding-right: 45px; }
+.ui-li-has-arrow .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-arrow { padding-right: 30px; }
+.ui-li-has-arrow.ui-li-has-count .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-arrow.ui-li-has-count { padding-right: 75px; }
+.ui-li-heading { font-size: 16px; font-weight: bold; display: block; margin: .6em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; }
+.ui-li-desc { font-size: 12px; font-weight: normal; display: block; margin: -.5em 0 .6em; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; }
+.ui-li-thumb, .ui-li-icon { position: absolute; left: 1px; top: 0; max-height: 80px; max-width: 80px; }
+.ui-li-icon { max-height: 40px; max-width: 40px; left: 10px; top: .9em; }
+.ui-li-thumb, .ui-li-icon, .ui-li-content { float: left; margin-right: 10px; }
+
+.ui-li-aside { float: right; width: 50%; text-align: right; margin: .3em 0; }
+@media all and (min-width: 480px){
+ .ui-li-aside { width: 45%; }
+}
+.ui-li-divider { cursor: default; }
+.ui-li-has-alt .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-alt { padding-right: 95px; }
+.ui-li-has-count .ui-li-count { position: absolute; font-size: 11px; font-weight: bold; padding: .2em .5em; top: 50%; margin-top: -.9em; right: 38px; }
+.ui-li-divider .ui-li-count, .ui-li-static .ui-li-count { right: 10px; }
+.ui-li-has-alt .ui-li-count { right: 55px; }
+.ui-li-link-alt { position: absolute; width: 40px; height: 100%; border-width: 0; border-left-width: 1px; top: 0; right: 0; margin: 0; padding: 0; z-index: 2; }
+.ui-li-link-alt .ui-btn { overflow: hidden; position: absolute; right: 8px; top: 50%; margin: -11px 0 0 0; border-bottom-width: 1px; z-index: -1;}
+.ui-li-link-alt .ui-btn-inner { padding: 0; height: 100%; position: absolute; width: 100%; top: 0; left: 0;}
+.ui-li-link-alt .ui-btn .ui-icon { right: 50%; margin-right: -9px; }
+
+.ui-listview * .ui-btn-inner > .ui-btn > .ui-btn-inner { border-top: 0px; }
+
+.ui-listview-filter { border-width: 0; overflow: hidden; margin: -15px -15px 15px -15px }
+.ui-listview-filter .ui-input-search { margin: 5px; width: auto; display: block; }
+
+.ui-listview-filter-inset { margin: -15px -5px -15px -5px; background: transparent; }
+.ui-li.ui-screen-hidden{display:none;}
+/* Odd iPad positioning issue. */
+@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) {
+ .ui-li .ui-btn-text { overflow: visible; }
+}/*
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
+*/
+label.ui-slider { display: block; }
+input.ui-slider-input { display: inline-block; width: 50px; }
+select.ui-slider-switch { display: none; }
+div.ui-slider { position: relative; display: inline-block; overflow: visible; height: 15px; padding: 0; margin: 0 2% 0 20px; top: 4px; width: 66%; }
+a.ui-slider-handle { position: absolute; z-index: 10; top: 50%; width: 28px; height: 28px; margin-top: -15px; margin-left: -15px; }
+a.ui-slider-handle .ui-btn-inner { padding-left: 0; padding-right: 0; }
+@media all and (min-width: 480px){
+ label.ui-slider { vertical-align: top; display: inline-block; width: 20%; margin: 0 2% 0 0; }
+ div.ui-slider { width: 45%; }
+}
+
+div.ui-slider-switch { height: 32px; overflow: hidden; margin-left: 0; }
+div.ui-slider-inneroffset { margin-left: 50%; position: absolute; top: 1px; height: 100%; width: 50%; }
+a.ui-slider-handle-snapping { -webkit-transition: left 100ms linear; }
+div.ui-slider-labelbg { position: absolute; top:0; margin: 0; border-width: 0; }
+div.ui-slider-switch div.ui-slider-labelbg-a { width: 60%; height: 100%; left: 0; }
+div.ui-slider-switch div.ui-slider-labelbg-b { width: 60%; height: 100%; right: 0; }
+.ui-slider-switch-a div.ui-slider-labelbg-a, .ui-slider-switch-b div.ui-slider-labelbg-b { z-index: -1; }
+.ui-slider-switch-a div.ui-slider-labelbg-b, .ui-slider-switch-b div.ui-slider-labelbg-a { z-index: 0; }
+
+div.ui-slider-switch a.ui-slider-handle { z-index: 20; width: 101%; height: 32px; margin-top: -18px; margin-left: -101%; }
+span.ui-slider-label { width: 100%; position: absolute;height: 32px; font-size: 16px; text-align: center; line-height: 2; background: none; border-color: transparent; }
+span.ui-slider-label-a { left: -100%; margin-right: -1px }
+span.ui-slider-label-b { right: -100%; margin-left: -1px }
+
--- a/css/local.css.php
+++ b/css/local.css.php
@@ -1,13 +1,15 @@
<?php
- header('Content-type: text/css');
- ob_start("compress");
- function compress($buffer) {
+
+header('Content-type: text/css');
+ob_start("compress");
+
+function compress($buffer) {
/* remove comments */
$buffer = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $buffer);
/* remove tabs, spaces, newlines, etc. */
$buffer = str_replace(array("\r\n", "\r", "\n", "\t", ' ', ' ', ' '), '', $buffer);
return $buffer;
- }
+}
echo '
.ui-li-thumb, .ui-li-icon { position: relative; }
@@ -86,7 +88,7 @@
}';
//if (false)
- echo '
+echo '
// adaptive layout from jQuery Mobile docs site
.type-interior .content-secondary {
border-right: 0;
@@ -113,7 +115,9 @@
padding:0;
margin: 0;
}
+ /* hires ahoy */
@media all and (min-width: 650px){
+
.content-secondary {
text-align: left;
float: left;
@@ -210,7 +214,8 @@
.type-interior .content-primary {
width: 60%;
}
-}';
- ob_end_flush();
+}
+';
+ob_end_flush();
?>
--- a/geo/route.kml.php
+++ b/geo/route.kml.php
--- a/geo/stops.kml.php
+++ b/geo/stops.kml.php
@@ -1,4 +1,5 @@
<?php
+include ('../include/common.inc.php');
header('Content-type: application/vnd.google-earth.kml+xml');
//http://wiki.openstreetmap.org/wiki/OpenLayers_Dynamic_KML
// Creates the KML/XML Document.
@@ -33,4 +34,5 @@
}
$kmlOutput = $dom->saveXML();
echo $kmlOutput;
-?>
+?>
+
--- a/include/common-auth.inc.php
+++ b/include/common-auth.inc.php
@@ -1,101 +1,33 @@
<?php
+require $basePath.'lib/openid.php';
+$openid = new LightOpenID($_SERVER['HTTP_HOST']);
+
+function login()
+{
+ global $openid;
+ if(!$openid->mode) {
+ $openid->required = array('contact/email');
+ $openid->identity = 'https://www.google.com/accounts/o8/id';
+ header('Location: ' . $openid->authUrl());
+ }
+ }
-/*
- * Copyright 2010,2011 Alexander Sadleir
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
+function auth()
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
-
-function getScheme() {
- $scheme = 'http';
- if (isset($_SERVER['HTTPS']) and $_SERVER['HTTPS'] == 'on') {
- $scheme .= 's';
- }
- return $scheme;
-}
-
-function getTrustRoot() {
- return sprintf("%s://%s:%s%s/", getScheme(), $_SERVER['SERVER_NAME'], $_SERVER['SERVER_PORT'], dirname($_SERVER['PHP_SELF']));
-}
-
-// Includes required files
-set_include_path(get_include_path() . PATH_SEPARATOR . $basePath . "lib/openid-php/");
-require_once "Auth/OpenID/Consumer.php";
-require_once "Auth/OpenID/FileStore.php";
-require_once "Auth/OpenID/AX.php";
-
-function login() {
- global $basePath;
- // Just tested this with/for Google, needs trying with others ...
- $oid_identifier = 'https://www.google.com/accounts/o8/id';
- // Create file storage area for OpenID data
- $store = new Auth_OpenID_FileStore(realpath($basePath) . '/lib/openid-php/oid_store');
- // Create OpenID consumer
- $consumer = new Auth_OpenID_Consumer($store);
- // Create an authentication request to the OpenID provider
- $auth = $consumer->begin($oid_identifier);
-
- // Create attribute request object
- // See http://code.google.com/apis/accounts/docs/OpenID.html#Parameters for parameters
- // Usage: make($type_uri, $count=1, $required=false, $alias=null)
- $attribute[] = Auth_OpenID_AX_AttrInfo :: make('http://axschema.org/contact/email', 2, 1, 'email');
- $attribute[] = Auth_OpenID_AX_AttrInfo :: make('http://axschema.org/namePerson/first', 1, 1, 'firstname');
- $attribute[] = Auth_OpenID_AX_AttrInfo :: make('http://axschema.org/namePerson/last', 1, 1, 'lastname');
-
- // Create AX fetch request
- $ax = new Auth_OpenID_AX_FetchRequest;
-
- // Add attributes to AX fetch request
- foreach ($attribute as $attr) {
- $ax->add($attr);
- }
-
- // Add AX fetch request to authentication request
- $auth->addExtension($ax);
- $_SESSION['returnURL'] = curPageURL();
- // Redirect to OpenID provider for authentication
- $url = $auth->redirectURL(getTrustRoot(), $_SESSION['returnURL']);
- header('Location: ' . $url);
-}
-
-function auth() {
- global $basePath;
- if ($_SESSION['authed'] == true)
- return true;
-
- // Create file storage area for OpenID data
- $store = new Auth_OpenID_FileStore(realpath($basePath) . '/lib/openid-php/oid_store');
- // Create OpenID consumer
- $consumer = new Auth_OpenID_Consumer($store);
- // Create an authentication request to the OpenID provider
- $response = $consumer->complete($_SESSION['returnURL']);
-
- if ($response->status == Auth_OpenID_SUCCESS) {
- // Get registration informations
- $ax = new Auth_OpenID_AX_FetchResponse();
- $obj = $ax->fromSuccessResponse($response);
- $email = $obj->data['http://axschema.org/contact/email'][0];
- var_dump($email);
- if ($email != "maxious@gmail.com") {
+{
+ if ($_SESSION['authed'] == true) return true;
+ global $openid;
+
+ if($openid->mode) {
+ $attr = $openid->getAttributes();
+ if ($attr["contact/email"] != "maxious@gmail.com") {
die("Access Denied");
+ } else {
+ $_SESSION['authed'] = true;
+ }
} else {
- $_SESSION['authed'] = true;
- }
- } else {
login();
- }
-}
-
-if ($_REQUEST['janrain_nonce'])
- auth();
+ }
+ }
?>
--- a/include/common-geo.inc.php
+++ b/include/common-geo.inc.php
@@ -18,58 +18,49 @@
// SELECT array_to_string(array(SELECT REPLACE(name_2006, ',', '\,') as name FROM suburbs order by name), ',')
$suburbs = explode(",", "Acton,Ainslie,Amaroo,Aranda,Banks,Barton,Belconnen,Bonner,Bonython,Braddon,Bruce,Calwell,Campbell,Chapman,Charnwood,Chifley,Chisholm,City,Conder,Cook,Curtin,Deakin,Dickson,Downer,Duffy,Dunlop,Evatt,Fadden,Farrer,Fisher,Florey,Flynn,Forrest,Franklin,Fraser,Fyshwick,Garran,Gilmore,Giralang,Gordon,Gowrie,Greenway,Griffith,Gungahlin,Hackett,Hall,Harrison,Hawker,Higgins,Holder,Holt,Hughes,Hume,Isaacs,Isabella Plains,Kaleen,Kambah,Kingston,Latham,Lawson,Lyneham,Lyons,Macarthur,Macgregor,Macquarie,Mawson,McKellar,Melba,Mitchell,Monash,Narrabundah,Ngunnawal,Nicholls,Oaks Estate,O'Connor,O'Malley,Oxley,Page,Palmerston,Parkes,Pearce,Phillip,Pialligo,Red Hill,Reid,Richardson,Rivett,Russell,Scullin,Spence,Stirling,Symonston,Tharwa,Theodore,Torrens,Turner,Wanniassa,Waramanga,Watson,Weetangera,Weston,Yarralumla");
-function staticmap($mapPoints, $zoom = 0, $markerImage = "iconb", $collapsible = true, $twotone = false) {
- global $basePath;
- $width = 300;
+function staticmap($mapPoints, $collapsible = true, $twotone = false, $path = false, $numbered = false) {
+
+ $markers = "";
$height = 300;
- $metersperpixel[9] = 305.492 * $width;
- $metersperpixel[10] = 152.746 * $width;
- $metersperpixel[11] = 76.373 * $width;
- $metersperpixel[12] = 38.187 * $width;
- $metersperpixel[13] = 19.093 * $width;
- $metersperpixel[14] = 9.547 * $width;
- $metersperpixel[15] = 4.773 * $width;
- //$metersperpixel[16] = 2.387 * $width;
- // $metersperpixel[17]=1.193*$width;
- $center = "";
- $markers = "";
- $mapwidthinmeters = 50;
+ $width = $height;
+ $index = 0;
if (sizeof($mapPoints) < 1)
return "map error";
if (sizeof($mapPoints) === 1) {
- if ($zoom == 0)
- $zoom = 14;
- $markers.= "{$mapPoints[0][0]},{$mapPoints[0][1]},$markerimage";
- $center = "{$mapPoints[0][0]},{$mapPoints[0][1]}";
- }
- else {
+ $markers = "markers={$mapPoints[0][0]},{$mapPoints[0][1]}";
+ } else {
+ if (!$numbered) {
+ $markers = "markers=";
+ }
+ if ($path) {
+ $markers.= "markers={$mapPoints[0][0]},{$mapPoints[0][1]}&path=";
+ }
foreach ($mapPoints as $index => $mapPoint) {
if ($twotone && $index == 0) {
- $markers.= $mapPoint[0] . "," . $mapPoint[1] . "," . "iconr" . ($index + 1);
- $center = "{$mapPoints[0][0]},{$mapPoints[0][1]}";
+ $markers = "markerd=color:red|".$mapPoint[0] . "," . $mapPoint[1]."&markers=";
} else {
- $markers.= $mapPoint[0] . "," . $mapPoint[1] . "," . $markerImage . ($index + 1);
- }
- if ($index + 1 != sizeof($mapPoints))
- $markers.= "|";
- $dist = distance($mapPoints[0][0], $mapPoint[0][1], $mapPoint[0], $mapPoint[1]);
- $mapwidthinmeters = ($dist > $mapwidthinmeters ? $dist : $mapwidthinmeters);
- $totalLat+= $mapPoint[0];
- $totalLon+= $mapPoint[1];
- }
- if ($zoom == 0) {
- $mapwidthinmeters = distance($minlat, $minlon, $minlat, $maxlon);
- foreach (array_reverse($metersperpixel, true) as $zoomLevel => $maxdistance) {
- if ($zoom == 0 && $mapwidthinmeters * 1.5 < ($maxdistance))
- $zoom = $zoomLevel;
+ if ($numbered) {
+ $label = ($index > 9 ? 9 : $index);
+ $markers.= "markers=label:$label|" . $mapPoint[0] . "," . $mapPoint[1];
+ if ($index + 1 != sizeof($mapPoints)) {
+ $markers.= "&";
+ }
+ } else {
+ $markers.= $mapPoint[0] . "," . $mapPoint[1];
+ if ($index + 1 != sizeof($mapPoints)) {
+ $markers.= "|";
+ }
+ }
+ $index++;
}
}
- $center = $totalLat / sizeof($mapPoints) . "," . $totalLon / sizeof($mapPoints);
}
$output = "";
if ($collapsible)
$output.= '<div class="map" data-role="collapsible" data-collapsed="true"><h3>Open Map...</h3>';
- $output.= '<img class="map" src="' . curPageURL() . '/' . $basePath . '/lib/staticmaplite/staticmap.php?center=' . $center . '&zoom=' . $zoom . '&size=' . $width . 'x' . $height . '&markers=' . $markers . '" width=' . $width . ' height=' . $height . '>';
+ if (isIOSDevice()) $output.= '<img class="hiresmap" src="http://maps.googleapis.com/maps/api/staticmap?size=' . $width . 'x' . $height . '&' . $markers . '&scale=2&sensor=true" width=' . $width . ' height=' . $height . '>';
+ else $output.= '<img class="lowresmap" src="http://maps.googleapis.com/maps/api/staticmap?size=' . $width . 'x' . $height . '&' . $markers . '&scale=1&format=jpg&sensor=true" width=' . $width . ' height=' . $height . '>';
+
if ($collapsible)
$output.= '</div>';
return $output;
--- a/include/common-request.inc.php
+++ b/include/common-request.inc.php
@@ -33,10 +33,18 @@
if (isset($_REQUEST['suburb'])) {
$suburb = $_REQUEST['suburb'];
}
-$pageKey = filter_var($_REQUEST['pageKey'], FILTER_SANITIZE_NUMBER_INT);
-$lat = filter_var($_REQUEST['lat'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
-$lon = filter_var($_REQUEST['lon'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
-$max_distance = filter_var($_REQUEST['radius'], FILTER_SANITIZE_NUMBER_INT);
+if (isset($_REQUEST['pageKey'])) {
+ $pageKey = filter_var($_REQUEST['pageKey'], FILTER_SANITIZE_NUMBER_INT);
+}
+if (isset($_REQUEST['lat'])) {
+ $lat = filter_var($_REQUEST['lat'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
+}
+if (isset($_REQUEST['lon'])) {
+ $lon = filter_var($_REQUEST['lon'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
+}
+if (isset($_REQUEST['radius'])) {
+ $max_distance = filter_var($_REQUEST['radius'], FILTER_SANITIZE_NUMBER_INT);
+}
if (isset($_REQUEST['numberSeries'])) {
$numberSeries = filter_var($_REQUEST['numberSeries'], FILTER_SANITIZE_NUMBER_INT);
}
--- a/include/common-template.inc.php
+++ b/include/common-template.inc.php
@@ -42,7 +42,7 @@
}
function include_header($pageTitle, $pageType, $opendiv = true, $geolocate = false, $datepicker = false) {
- global $basePath, $serviceAlertsEnabled;
+ global $basePath, $GTFSREnabled;
echo '
<!DOCTYPE html>
<html lang="en">
@@ -54,14 +54,15 @@
<link rel="dns-prefetch" href="//code.jquery.com">
<link rel="dns-prefetch" href="//ajax.googleapis.com">
<link rel="stylesheet" href="' . $basePath . 'css/jquery-ui-1.8.12.custom.css" />';
+ $jqmVersion = "1.0rc1";
if (isDebugServer()) {
- $jqmcss = $basePath . 'css/jquery.mobile-1.0b3.css';
- $jqjs = $basePath . 'js/jquery-1.6.2.min.js';
- $jqmjs = $basePath . 'js/jquery.mobile-1.0b3.js';
+ $jqmcss = $basePath . "css/jquery.mobile-$jqmVersion.css";
+ $jqjs = $basePath . "js/jquery-1.6.2.min.js";
+ $jqmjs = $basePath . "js/jquery.mobile-$jqmVersion.js";
} else {
- $jqmcss = "//code.jquery.com/mobile/1.0b3/jquery.mobile-1.0b3.min.css";
+ $jqmcss = "//code.jquery.com/mobile/1.0b3/jquery.mobile-$jqmVersion.min.css";
$jqjs = "//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js";
- $jqmjs = "//code.jquery.com/mobile/1.0b3/jquery.mobile-1.0b3.min.js";
+ $jqmjs = "//code.jquery.com/mobile/1.0b3/jquery.mobile-$jqmVersion.min.js";
}
echo '<link rel="stylesheet" href="' . $jqmcss . '" />
<script src="' . $jqjs . '"></script>
@@ -103,7 +104,7 @@
}';
echo '</style>';
echo '<link rel="stylesheet" href="' . $basePath . 'css/local.css.php" />';
- if (strstr($_SERVER['HTTP_USER_AGENT'], 'iPhone') || strstr($_SERVER['HTTP_USER_AGENT'], 'iPod') || strstr($_SERVER['HTTP_USER_AGENT'], 'iPad')) {
+ if (isIOSDevice()){
echo '<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<link rel="apple-touch-startup-image" href="startup.png" />
@@ -179,9 +180,11 @@
}
if ($GTFSREnabled) {
$serviceAlerts = getServiceAlertsAsArray("agency", "0");
- foreach ($serviceAlerts['entity'] as $entity) {
- echo "<div id='servicewarning'>" . date("F j, g:i a", strtotime($entity['alert']['active_period'][0]['start'])) . " to " . date("F j, g:i a", strtotime($entity['alert']['active_period'][0]['end'])) . "{$entity['alert']['header_text']['translation'][0]['text']}<br>Warning: {$entity['alert']['description_text']['translation'][0]['text']}
- <br><a href='{$entity['alert']['url']['translation'][0]['text']}'>Source</a> </div>";
+ if (isset($serviceAlerts['entity']) && sizeof($serviceAlerts['entity']) > 0) {
+ foreach ($serviceAlerts['entity'] as $entity) {
+ echo "<div id='servicewarning'>" . date("F j, g:i a", strtotime($entity['alert']['active_period'][0]['start'])) . " to " . date("F j, g:i a", strtotime($entity['alert']['active_period'][0]['end'])) . "{$entity['alert']['header_text']['translation'][0]['text']}<br>Warning: {$entity['alert']['description_text']['translation'][0]['text']}
+ <br><a href='{$entity['alert']['url']['translation'][0]['text']}'>Source</a> </div>";
+ }
}
}
}
--- a/include/common.inc.php
+++ b/include/common.inc.php
@@ -48,6 +48,9 @@
$basePath = "";
if (strstr($_SERVER['PHP_SELF'], "labs/")
|| strstr($_SERVER['PHP_SELF'], "myway/")
+ || strstr($_SERVER['PHP_SELF'], "lib/")
+ || strstr($_SERVER['PHP_SELF'], "geo/")
+ || strstr($_SERVER['PHP_SELF'], "include/")
|| strstr($_SERVER['PHP_SELF'], "servicealerts/"))
$basePath = "../";
@@ -81,31 +84,15 @@
if (isDebug($debugReason))
echo "\n<!-- " . date(DATE_RFC822) . "\n $msg -->\n";
}
-
+function isIOSDevice() {
+ return strstr($_SERVER['HTTP_USER_AGENT'], 'iPhone') || strstr($_SERVER['HTTP_USER_AGENT'], 'iPod') || strstr($_SERVER['HTTP_USER_AGENT'], 'iPad');
+}
function isJQueryMobileDevice() {
// http://forum.jquery.com/topic/what-is-the-best-way-to-detect-all-useragents-which-can-handle-jquery-mobile#14737000002087897
$user_agent = $_SERVER['HTTP_USER_AGENT'];
return preg_match('/iphone/i', $user_agent) || preg_match('/android/i', $user_agent) || preg_match('/webos/i', $user_agent) || preg_match('/ios/i', $user_agent) || preg_match('/bada/i', $user_agent) || preg_match('/maemo/i', $user_agent) || preg_match('/meego/i', $user_agent) || preg_match('/fennec/i', $user_agent) || (preg_match('/symbian/i', $user_agent) && preg_match('/s60/i', $user_agent) && $browser['majorver'] >= 5) || (preg_match('/symbian/i', $user_agent) && preg_match('/platform/i', $user_agent) && $browser['majorver'] >= 3) || (preg_match('/blackberry/i', $user_agent) && $browser['majorver'] >= 5) || (preg_match('/opera mobile/i', $user_agent) && $browser['majorver'] >= 10) || (preg_match('/opera mini/i', $user_agent) && $browser['majorver'] >= 5);
}
-function isFastDevice() {
- $ua = $_SERVER['HTTP_USER_AGENT'];
- $fastDevices = Array(
- "Mozilla/5.0 (X11;",
- "Mozilla/5.0 (Windows;",
- "Mozilla/5.0 (iP",
- "Mozilla/5.0 (Linux; U; Android",
- "Mozilla/4.0 (compatible; MSIE"
- );
- $slowDevices = Array(
- "J2ME",
- "MIDP",
- "Opera/",
- "Mozilla/2.0 (compatible;",
- "Mozilla/3.0 (compatible;"
- );
- return true;
-}
function array_flatten($a, $f = array()) {
if (!$a || !is_array($a))
--- a/include/db/servicealert-dao.inc.php
+++ b/include/db/servicealert-dao.inc.php
@@ -32,7 +32,7 @@
function getServiceAlert($alertID) {
global $conn;
- $query = "SELECT id,extract('epoch', start) as start, extract('epoch', end) as end,cause,effect,header,description,url from servicealerts_alerts where id = :servicealert_id";
+ $query = "SELECT id,extract('epoch' from start) as start, extract('epoch' from end) as end,cause,effect,header,description,url from servicealerts_alerts where id = :servicealert_id";
debug($query, "database");
$query = $conn->prepare($query);
$query->bindParam(":servicealert_id", $alertID);
@@ -87,7 +87,7 @@
function getCurrentAlerts() {
global $conn;
- $query = "SELECT id,extract('epoch', start) as start, extract('epoch', end) as end,cause,effect,header,description,url from servicealerts_alerts where NOW() > start and NOW() < \"end\"";
+ $query = "SELECT id,extract('epoch' from start) as start, extract('epoch' from end) as end,cause,effect,header,description,url from servicealerts_alerts where NOW() > start and NOW() < \"end\"";
// debug($query, "database");
$query = $conn->prepare($query);
$query->execute();
@@ -100,7 +100,7 @@
function getFutureAlerts() {
global $conn;
- $query = "SELECT id,extract('epoch', start) as start, extract('epoch', end) as end,cause,effect,header,description,url from servicealerts_alerts where NOW() > start or NOW() < \"end\"";
+ $query = "SELECT id,extract('epoch' from start) as start, extract('epoch' from end) as end,cause,effect,header,description,url from servicealerts_alerts where NOW() > start or NOW() < \"end\"";
// debug($query, "database");
$query = $conn->prepare($query);
$query->execute();
--- a/js/jquery.mobile-1.0b3.js
+++ /dev/null
@@ -1,6658 +1,1 @@
-/*!
- * jQuery Mobile v1.0b3
- * http://jquerymobile.com/
- *
- * Copyright 2010, jQuery Project
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- */
-/*!
- * jQuery UI Widget @VERSION
- *
- * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Widget
- */
-(function( $, undefined ) {
-// jQuery 1.4+
-if ( $.cleanData ) {
- var _cleanData = $.cleanData;
- $.cleanData = function( elems ) {
- for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
- $( elem ).triggerHandler( "remove" );
- }
- _cleanData( elems );
- };
-} else {
- var _remove = $.fn.remove;
- $.fn.remove = function( selector, keepData ) {
- return this.each(function() {
- if ( !keepData ) {
- if ( !selector || $.filter( selector, [ this ] ).length ) {
- $( "*", this ).add( [ this ] ).each(function() {
- $( this ).triggerHandler( "remove" );
- });
- }
- }
- return _remove.call( $(this), selector, keepData );
- });
- };
-}
-
-$.widget = function( name, base, prototype ) {
- var namespace = name.split( "." )[ 0 ],
- fullName;
- name = name.split( "." )[ 1 ];
- fullName = namespace + "-" + name;
-
- if ( !prototype ) {
- prototype = base;
- base = $.Widget;
- }
-
- // create selector for plugin
- $.expr[ ":" ][ fullName ] = function( elem ) {
- return !!$.data( elem, name );
- };
-
- $[ namespace ] = $[ namespace ] || {};
- $[ namespace ][ name ] = function( options, element ) {
- // allow instantiation without initializing for simple inheritance
- if ( arguments.length ) {
- this._createWidget( options, element );
- }
- };
-
- var basePrototype = new base();
- // we need to make the options hash a property directly on the new instance
- // otherwise we'll modify the options hash on the prototype that we're
- // inheriting from
-// $.each( basePrototype, function( key, val ) {
-// if ( $.isPlainObject(val) ) {
-// basePrototype[ key ] = $.extend( {}, val );
-// }
-// });
- basePrototype.options = $.extend( true, {}, basePrototype.options );
- $[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
- namespace: namespace,
- widgetName: name,
- widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
- widgetBaseClass: fullName
- }, prototype );
-
- $.widget.bridge( name, $[ namespace ][ name ] );
-};
-
-$.widget.bridge = function( name, object ) {
- $.fn[ name ] = function( options ) {
- var isMethodCall = typeof options === "string",
- args = Array.prototype.slice.call( arguments, 1 ),
- returnValue = this;
-
- // allow multiple hashes to be passed on init
- options = !isMethodCall && args.length ?
- $.extend.apply( null, [ true, options ].concat(args) ) :
- options;
-
- // prevent calls to internal methods
- if ( isMethodCall && options.charAt( 0 ) === "_" ) {
- return returnValue;
- }
-
- if ( isMethodCall ) {
- this.each(function() {
- var instance = $.data( this, name );
- if ( !instance ) {
- throw "cannot call methods on " + name + " prior to initialization; " +
- "attempted to call method '" + options + "'";
- }
- if ( !$.isFunction( instance[options] ) ) {
- throw "no such method '" + options + "' for " + name + " widget instance";
- }
- var methodValue = instance[ options ].apply( instance, args );
- if ( methodValue !== instance && methodValue !== undefined ) {
- returnValue = methodValue;
- return false;
- }
- });
- } else {
- this.each(function() {
- var instance = $.data( this, name );
- if ( instance ) {
- instance.option( options || {} )._init();
- } else {
- $.data( this, name, new object( options, this ) );
- }
- });
- }
-
- return returnValue;
- };
-};
-
-$.Widget = function( options, element ) {
- // allow instantiation without initializing for simple inheritance
- if ( arguments.length ) {
- this._createWidget( options, element );
- }
-};
-
-$.Widget.prototype = {
- widgetName: "widget",
- widgetEventPrefix: "",
- options: {
- disabled: false
- },
- _createWidget: function( options, element ) {
- // $.widget.bridge stores the plugin instance, but we do it anyway
- // so that it's stored even before the _create function runs
- $.data( element, this.widgetName, this );
- this.element = $( element );
- this.options = $.extend( true, {},
- this.options,
- this._getCreateOptions(),
- options );
-
- var self = this;
- this.element.bind( "remove." + this.widgetName, function() {
- self.destroy();
- });
-
- this._create();
- this._trigger( "create" );
- this._init();
- },
- _getCreateOptions: function() {
- var options = {};
- if ( $.metadata ) {
- options = $.metadata.get( element )[ this.widgetName ];
- }
- return options;
- },
- _create: function() {},
- _init: function() {},
-
- destroy: function() {
- this.element
- .unbind( "." + this.widgetName )
- .removeData( this.widgetName );
- this.widget()
- .unbind( "." + this.widgetName )
- .removeAttr( "aria-disabled" )
- .removeClass(
- this.widgetBaseClass + "-disabled " +
- "ui-state-disabled" );
- },
-
- widget: function() {
- return this.element;
- },
-
- option: function( key, value ) {
- var options = key;
-
- if ( arguments.length === 0 ) {
- // don't return a reference to the internal hash
- return $.extend( {}, this.options );
- }
-
- if (typeof key === "string" ) {
- if ( value === undefined ) {
- return this.options[ key ];
- }
- options = {};
- options[ key ] = value;
- }
-
- this._setOptions( options );
-
- return this;
- },
- _setOptions: function( options ) {
- var self = this;
- $.each( options, function( key, value ) {
- self._setOption( key, value );
- });
-
- return this;
- },
- _setOption: function( key, value ) {
- this.options[ key ] = value;
-
- if ( key === "disabled" ) {
- this.widget()
- [ value ? "addClass" : "removeClass"](
- this.widgetBaseClass + "-disabled" + " " +
- "ui-state-disabled" )
- .attr( "aria-disabled", value );
- }
-
- return this;
- },
-
- enable: function() {
- return this._setOption( "disabled", false );
- },
- disable: function() {
- return this._setOption( "disabled", true );
- },
-
- _trigger: function( type, event, data ) {
- var callback = this.options[ type ];
-
- event = $.Event( event );
- event.type = ( type === this.widgetEventPrefix ?
- type :
- this.widgetEventPrefix + type ).toLowerCase();
- data = data || {};
-
- // copy original event properties over to the new event
- // this would happen if we could call $.event.fix instead of $.Event
- // but we don't have a way to force an event to be fixed multiple times
- if ( event.originalEvent ) {
- for ( var i = $.event.props.length, prop; i; ) {
- prop = $.event.props[ --i ];
- event[ prop ] = event.originalEvent[ prop ];
- }
- }
-
- this.element.trigger( event, data );
-
- return !( $.isFunction(callback) &&
- callback.call( this.element[0], event, data ) === false ||
- event.isDefaultPrevented() );
- }
-};
-
-})( jQuery );
-/*
-* jQuery Mobile Framework : widget factory extentions for mobile
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-
-(function( $, undefined ) {
-
-$.widget( "mobile.widget", {
- _getCreateOptions: function() {
-
- var elem = this.element,
- options = {};
-
- $.each( this.options, function( option ) {
-
- var value = elem.jqmData( option.replace( /[A-Z]/g, function( c ) {
- return "-" + c.toLowerCase();
- })
- );
-
- if ( value !== undefined ) {
- options[ option ] = value;
- }
- });
-
- return options;
- }
-});
-
-})( jQuery );
-/*
-* jQuery Mobile Framework : resolution and CSS media query related helpers and behavior
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-(function( $, undefined ) {
-
-var $window = $( window ),
- $html = $( "html" );
-
-/* $.mobile.media method: pass a CSS media type or query and get a bool return
- note: this feature relies on actual media query support for media queries, though types will work most anywhere
- examples:
- $.mobile.media('screen') //>> tests for screen media type
- $.mobile.media('screen and (min-width: 480px)') //>> tests for screen media type with window width > 480px
- $.mobile.media('@media screen and (-webkit-min-device-pixel-ratio: 2)') //>> tests for webkit 2x pixel ratio (iPhone 4)
-*/
-$.mobile.media = (function() {
- // TODO: use window.matchMedia once at least one UA implements it
- var cache = {},
- testDiv = $( "<div id='jquery-mediatest'>" ),
- fakeBody = $( "<body>" ).append( testDiv );
-
- return function( query ) {
- if ( !( query in cache ) ) {
- var styleBlock = document.createElement( "style" ),
- cssrule = "@media " + query + " { #jquery-mediatest { position:absolute; } }";
-
- //must set type for IE!
- styleBlock.type = "text/css";
-
- if ( styleBlock.styleSheet ){
- styleBlock.styleSheet.cssText = cssrule;
- } else {
- styleBlock.appendChild( document.createTextNode(cssrule) );
- }
-
- $html.prepend( fakeBody ).prepend( styleBlock );
- cache[ query ] = testDiv.css( "position" ) === "absolute";
- fakeBody.add( styleBlock ).remove();
- }
- return cache[ query ];
- };
-})();
-
-})(jQuery);/*
-* jQuery Mobile Framework : support tests
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
-*/
-
-(function( $, undefined ) {
-
-var fakeBody = $( "<body>" ).prependTo( "html" ),
- fbCSS = fakeBody[ 0 ].style,
- vendors = [ "Webkit", "Moz", "O" ],
- webos = "palmGetResource" in window, //only used to rule out scrollTop
- bb = window.blackberry; //only used to rule out box shadow, as it's filled opaque on BB
-
-// thx Modernizr
-function propExists( prop ) {
- var uc_prop = prop.charAt( 0 ).toUpperCase() + prop.substr( 1 ),
- props = ( prop + " " + vendors.join( uc_prop + " " ) + uc_prop ).split( " " );
-
- for ( var v in props ){
- if ( fbCSS[ props[ v ] ] !== undefined ) {
- return true;
- }
- }
-}
-
-// Test for dynamic-updating base tag support ( allows us to avoid href,src attr rewriting )
-function baseTagTest() {
- var fauxBase = location.protocol + "//" + location.host + location.pathname + "ui-dir/",
- base = $( "head base" ),
- fauxEle = null,
- href = "",
- link, rebase;
-
- if ( !base.length ) {
- base = fauxEle = $( "<base>", { "href": fauxBase }).appendTo( "head" );
- } else {
- href = base.attr( "href" );
- }
-
- link = $( "<a href='testurl'></a>" ).prependTo( fakeBody );
- rebase = link[ 0 ].href;
- base[ 0 ].href = href ? href : location.pathname;
-
- if ( fauxEle ) {
- fauxEle.remove();
- }
- return rebase.indexOf( fauxBase ) === 0;
-}
-
-
-// non-UA-based IE version check by James Padolsey, modified by jdalton - from http://gist.github.com/527683
-// allows for inclusion of IE 6+, including Windows Mobile 7
-$.mobile.browser = {};
-$.mobile.browser.ie = (function() {
- var v = 3,
- div = document.createElement( "div" ),
- a = div.all || [];
-
- while ( div.innerHTML = "<!--[if gt IE " + ( ++v ) + "]><br><![endif]-->", a[ 0 ] );
-
- return v > 4 ? v : !v;
-})();
-
-
-$.extend( $.support, {
- orientation: "orientation" in window,
- touch: "ontouchend" in document,
- cssTransitions: "WebKitTransitionEvent" in window,
- pushState: "pushState" in history && "replaceState" in history,
- mediaquery: $.mobile.media( "only all" ),
- cssPseudoElement: !!propExists( "content" ),
- touchOverflow: !!propExists( "overflowScrolling" ),
- boxShadow: !!propExists( "boxShadow" ) && !bb,
- scrollTop: ( "pageXOffset" in window || "scrollTop" in document.documentElement || "scrollTop" in fakeBody[ 0 ] ) && !webos,
- dynamicBaseTag: baseTagTest(),
- // TODO: This is a weak test. We may want to beef this up later.
- eventCapture: "addEventListener" in document
-});
-
-fakeBody.remove();
-
-
-// $.mobile.ajaxBlacklist is used to override ajaxEnabled on platforms that have known conflicts with hash history updates (BB5, Symbian)
-// or that generally work better browsing in regular http for full page refreshes (Opera Mini)
-// Note: This detection below is used as a last resort.
-// We recommend only using these detection methods when all other more reliable/forward-looking approaches are not possible
-var nokiaLTE7_3 = (function(){
-
- var ua = window.navigator.userAgent;
-
- //The following is an attempt to match Nokia browsers that are running Symbian/s60, with webkit, version 7.3 or older
- return ua.indexOf( "Nokia" ) > -1 &&
- ( ua.indexOf( "Symbian/3" ) > -1 || ua.indexOf( "Series60/5" ) > -1 ) &&
- ua.indexOf( "AppleWebKit" ) > -1 &&
- ua.match( /(BrowserNG|NokiaBrowser)\/7\.[0-3]/ );
-})();
-
-$.mobile.ajaxBlacklist =
- // BlackBerry browsers, pre-webkit
- window.blackberry && !window.WebKitPoint ||
- // Opera Mini
- window.operamini && Object.prototype.toString.call( window.operamini ) === "[object OperaMini]" ||
- // Symbian webkits pre 7.3
- nokiaLTE7_3;
-
-// Lastly, this workaround is the only way we've found so far to get pre 7.3 Symbian webkit devices
-// to render the stylesheets when they're referenced before this script, as we'd recommend doing.
-// This simply reappends the CSS in place, which for some reason makes it apply
-if ( nokiaLTE7_3 ) {
- $(function() {
- $( "head link[rel=stylesheet]" ).attr( "rel", "alternate stylesheet" ).attr( "rel", "stylesheet" );
- });
-}
-
-// For ruling out shadows via css
-if ( !$.support.boxShadow ) {
- $( "html" ).addClass( "ui-mobile-nosupport-boxshadow" );
-}
-
-})( jQuery );/*
-* jQuery Mobile Framework : "mouse" plugin
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-
-// This plugin is an experiment for abstracting away the touch and mouse
-// events so that developers don't have to worry about which method of input
-// the device their document is loaded on supports.
-//
-// The idea here is to allow the developer to register listeners for the
-// basic mouse events, such as mousedown, mousemove, mouseup, and click,
-// and the plugin will take care of registering the correct listeners
-// behind the scenes to invoke the listener at the fastest possible time
-// for that device, while still retaining the order of event firing in
-// the traditional mouse environment, should multiple handlers be registered
-// on the same element for different events.
-//
-// The current version exposes the following virtual events to jQuery bind methods:
-// "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel"
-
-(function( $, window, document, undefined ) {
-
-var dataPropertyName = "virtualMouseBindings",
- touchTargetPropertyName = "virtualTouchID",
- virtualEventNames = "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split( " " ),
- touchEventProps = "clientX clientY pageX pageY screenX screenY".split( " " ),
- activeDocHandlers = {},
- resetTimerID = 0,
- startX = 0,
- startY = 0,
- didScroll = false,
- clickBlockList = [],
- blockMouseTriggers = false,
- blockTouchTriggers = false,
- eventCaptureSupported = $.support.eventCapture,
- $document = $( document ),
- nextTouchID = 1,
- lastTouchID = 0;
-
-$.vmouse = {
- moveDistanceThreshold: 10,
- clickDistanceThreshold: 10,
- resetTimerDuration: 1500
-};
-
-function getNativeEvent( event ) {
-
- while ( event && typeof event.originalEvent !== "undefined" ) {
- event = event.originalEvent;
- }
- return event;
-}
-
-function createVirtualEvent( event, eventType ) {
-
- var t = event.type,
- oe, props, ne, prop, ct, touch, i, j;
-
- event = $.Event(event);
- event.type = eventType;
-
- oe = event.originalEvent;
- props = $.event.props;
-
- // copy original event properties over to the new event
- // this would happen if we could call $.event.fix instead of $.Event
- // but we don't have a way to force an event to be fixed multiple times
- if ( oe ) {
- for ( i = props.length, prop; i; ) {
- prop = props[ --i ];
- event[ prop ] = oe[ prop ];
- }
- }
-
- if ( t.search(/^touch/) !== -1 ) {
- ne = getNativeEvent( oe );
- t = ne.touches;
- ct = ne.changedTouches;
- touch = ( t && t.length ) ? t[0] : ( (ct && ct.length) ? ct[ 0 ] : undefined );
-
- if ( touch ) {
- for ( j = 0, len = touchEventProps.length; j < len; j++){
- prop = touchEventProps[ j ];
- event[ prop ] = touch[ prop ];
- }
- }
- }
-
- return event;
-}
-
-function getVirtualBindingFlags( element ) {
-
- var flags = {},
- b, k;
-
- while ( element ) {
-
- b = $.data( element, dataPropertyName );
-
- for ( k in b ) {
- if ( b[ k ] ) {
- flags[ k ] = flags.hasVirtualBinding = true;
- }
- }
- element = element.parentNode;
- }
- return flags;
-}
-
-function getClosestElementWithVirtualBinding( element, eventType ) {
- var b;
- while ( element ) {
-
- b = $.data( element, dataPropertyName );
-
- if ( b && ( !eventType || b[ eventType ] ) ) {
- return element;
- }
- element = element.parentNode;
- }
- return null;
-}
-
-function enableTouchBindings() {
- blockTouchTriggers = false;
-}
-
-function disableTouchBindings() {
- blockTouchTriggers = true;
-}
-
-function enableMouseBindings() {
- lastTouchID = 0;
- clickBlockList.length = 0;
- blockMouseTriggers = false;
-
- // When mouse bindings are enabled, our
- // touch bindings are disabled.
- disableTouchBindings();
-}
-
-function disableMouseBindings() {
- // When mouse bindings are disabled, our
- // touch bindings are enabled.
- enableTouchBindings();
-}
-
-function startResetTimer() {
- clearResetTimer();
- resetTimerID = setTimeout(function(){
- resetTimerID = 0;
- enableMouseBindings();
- }, $.vmouse.resetTimerDuration );
-}
-
-function clearResetTimer() {
- if ( resetTimerID ){
- clearTimeout( resetTimerID );
- resetTimerID = 0;
- }
-}
-
-function triggerVirtualEvent( eventType, event, flags ) {
- var ve;
-
- if ( ( flags && flags[ eventType ] ) ||
- ( !flags && getClosestElementWithVirtualBinding( event.target, eventType ) ) ) {
-
- ve = createVirtualEvent( event, eventType );
-
- $( event.target).trigger( ve );
- }
-
- return ve;
-}
-
-function mouseEventCallback( event ) {
- var touchID = $.data(event.target, touchTargetPropertyName);
-
- if ( !blockMouseTriggers && ( !lastTouchID || lastTouchID !== touchID ) ){
- var ve = triggerVirtualEvent( "v" + event.type, event );
- if ( ve ) {
- if ( ve.isDefaultPrevented() ) {
- event.preventDefault();
- }
- if ( ve.isPropagationStopped() ) {
- event.stopPropagation();
- }
- if ( ve.isImmediatePropagationStopped() ) {
- event.stopImmediatePropagation();
- }
- }
- }
-}
-
-function handleTouchStart( event ) {
-
- var touches = getNativeEvent( event ).touches,
- target, flags;
-
- if ( touches && touches.length === 1 ) {
-
- target = event.target;
- flags = getVirtualBindingFlags( target );
-
- if ( flags.hasVirtualBinding ) {
-
- lastTouchID = nextTouchID++;
- $.data( target, touchTargetPropertyName, lastTouchID );
-
- clearResetTimer();
-
- disableMouseBindings();
- didScroll = false;
-
- var t = getNativeEvent( event ).touches[ 0 ];
- startX = t.pageX;
- startY = t.pageY;
-
- triggerVirtualEvent( "vmouseover", event, flags );
- triggerVirtualEvent( "vmousedown", event, flags );
- }
- }
-}
-
-function handleScroll( event ) {
- if ( blockTouchTriggers ) {
- return;
- }
-
- if ( !didScroll ) {
- triggerVirtualEvent( "vmousecancel", event, getVirtualBindingFlags( event.target ) );
- }
-
- didScroll = true;
- startResetTimer();
-}
-
-function handleTouchMove( event ) {
- if ( blockTouchTriggers ) {
- return;
- }
-
- var t = getNativeEvent( event ).touches[ 0 ],
- didCancel = didScroll,
- moveThreshold = $.vmouse.moveDistanceThreshold;
- didScroll = didScroll ||
- ( Math.abs(t.pageX - startX) > moveThreshold ||
- Math.abs(t.pageY - startY) > moveThreshold ),
- flags = getVirtualBindingFlags( event.target );
-
- if ( didScroll && !didCancel ) {
- triggerVirtualEvent( "vmousecancel", event, flags );
- }
-
- triggerVirtualEvent( "vmousemove", event, flags );
- startResetTimer();
-}
-
-function handleTouchEnd( event ) {
- if ( blockTouchTriggers ) {
- return;
- }
-
- disableTouchBindings();
-
- var flags = getVirtualBindingFlags( event.target ),
- t;
- triggerVirtualEvent( "vmouseup", event, flags );
-
- if ( !didScroll ) {
- var ve = triggerVirtualEvent( "vclick", event, flags );
- if ( ve && ve.isDefaultPrevented() ) {
- // The target of the mouse events that follow the touchend
- // event don't necessarily match the target used during the
- // touch. This means we need to rely on coordinates for blocking
- // any click that is generated.
- t = getNativeEvent( event ).changedTouches[ 0 ];
- clickBlockList.push({
- touchID: lastTouchID,
- x: t.clientX,
- y: t.clientY
- });
-
- // Prevent any mouse events that follow from triggering
- // virtual event notifications.
- blockMouseTriggers = true;
- }
- }
- triggerVirtualEvent( "vmouseout", event, flags);
- didScroll = false;
-
- startResetTimer();
-}
-
-function hasVirtualBindings( ele ) {
- var bindings = $.data( ele, dataPropertyName ),
- k;
-
- if ( bindings ) {
- for ( k in bindings ) {
- if ( bindings[ k ] ) {
- return true;
- }
- }
- }
- return false;
-}
-
-function dummyMouseHandler(){}
-
-function getSpecialEventObject( eventType ) {
- var realType = eventType.substr( 1 );
-
- return {
- setup: function( data, namespace ) {
- // If this is the first virtual mouse binding for this element,
- // add a bindings object to its data.
-
- if ( !hasVirtualBindings( this ) ) {
- $.data( this, dataPropertyName, {});
- }
-
- // If setup is called, we know it is the first binding for this
- // eventType, so initialize the count for the eventType to zero.
- var bindings = $.data( this, dataPropertyName );
- bindings[ eventType ] = true;
-
- // If this is the first virtual mouse event for this type,
- // register a global handler on the document.
-
- activeDocHandlers[ eventType ] = ( activeDocHandlers[ eventType ] || 0 ) + 1;
-
- if ( activeDocHandlers[ eventType ] === 1 ) {
- $document.bind( realType, mouseEventCallback );
- }
-
- // Some browsers, like Opera Mini, won't dispatch mouse/click events
- // for elements unless they actually have handlers registered on them.
- // To get around this, we register dummy handlers on the elements.
-
- $( this ).bind( realType, dummyMouseHandler );
-
- // For now, if event capture is not supported, we rely on mouse handlers.
- if ( eventCaptureSupported ) {
- // If this is the first virtual mouse binding for the document,
- // register our touchstart handler on the document.
-
- activeDocHandlers[ "touchstart" ] = ( activeDocHandlers[ "touchstart" ] || 0) + 1;
-
- if (activeDocHandlers[ "touchstart" ] === 1) {
- $document.bind( "touchstart", handleTouchStart )
- .bind( "touchend", handleTouchEnd )
-
- // On touch platforms, touching the screen and then dragging your finger
- // causes the window content to scroll after some distance threshold is
- // exceeded. On these platforms, a scroll prevents a click event from being
- // dispatched, and on some platforms, even the touchend is suppressed. To
- // mimic the suppression of the click event, we need to watch for a scroll
- // event. Unfortunately, some platforms like iOS don't dispatch scroll
- // events until *AFTER* the user lifts their finger (touchend). This means
- // we need to watch both scroll and touchmove events to figure out whether
- // or not a scroll happenens before the touchend event is fired.
-
- .bind( "touchmove", handleTouchMove )
- .bind( "scroll", handleScroll );
- }
- }
- },
-
- teardown: function( data, namespace ) {
- // If this is the last virtual binding for this eventType,
- // remove its global handler from the document.
-
- --activeDocHandlers[ eventType ];
-
- if ( !activeDocHandlers[ eventType ] ) {
- $document.unbind( realType, mouseEventCallback );
- }
-
- if ( eventCaptureSupported ) {
- // If this is the last virtual mouse binding in existence,
- // remove our document touchstart listener.
-
- --activeDocHandlers[ "touchstart" ];
-
- if ( !activeDocHandlers[ "touchstart" ] ) {
- $document.unbind( "touchstart", handleTouchStart )
- .unbind( "touchmove", handleTouchMove )
- .unbind( "touchend", handleTouchEnd )
- .unbind( "scroll", handleScroll );
- }
- }
-
- var $this = $( this ),
- bindings = $.data( this, dataPropertyName );
-
- // teardown may be called when an element was
- // removed from the DOM. If this is the case,
- // jQuery core may have already stripped the element
- // of any data bindings so we need to check it before
- // using it.
- if ( bindings ) {
- bindings[ eventType ] = false;
- }
-
- // Unregister the dummy event handler.
-
- $this.unbind( realType, dummyMouseHandler );
-
- // If this is the last virtual mouse binding on the
- // element, remove the binding data from the element.
-
- if ( !hasVirtualBindings( this ) ) {
- $this.removeData( dataPropertyName );
- }
- }
- };
-}
-
-// Expose our custom events to the jQuery bind/unbind mechanism.
-
-for ( var i = 0; i < virtualEventNames.length; i++ ){
- $.event.special[ virtualEventNames[ i ] ] = getSpecialEventObject( virtualEventNames[ i ] );
-}
-
-// Add a capture click handler to block clicks.
-// Note that we require event capture support for this so if the device
-// doesn't support it, we punt for now and rely solely on mouse events.
-if ( eventCaptureSupported ) {
- document.addEventListener( "click", function( e ){
- var cnt = clickBlockList.length,
- target = e.target,
- x, y, ele, i, o, touchID;
-
- if ( cnt ) {
- x = e.clientX;
- y = e.clientY;
- threshold = $.vmouse.clickDistanceThreshold;
-
- // The idea here is to run through the clickBlockList to see if
- // the current click event is in the proximity of one of our
- // vclick events that had preventDefault() called on it. If we find
- // one, then we block the click.
- //
- // Why do we have to rely on proximity?
- //
- // Because the target of the touch event that triggered the vclick
- // can be different from the target of the click event synthesized
- // by the browser. The target of a mouse/click event that is syntehsized
- // from a touch event seems to be implementation specific. For example,
- // some browsers will fire mouse/click events for a link that is near
- // a touch event, even though the target of the touchstart/touchend event
- // says the user touched outside the link. Also, it seems that with most
- // browsers, the target of the mouse/click event is not calculated until the
- // time it is dispatched, so if you replace an element that you touched
- // with another element, the target of the mouse/click will be the new
- // element underneath that point.
- //
- // Aside from proximity, we also check to see if the target and any
- // of its ancestors were the ones that blocked a click. This is necessary
- // because of the strange mouse/click target calculation done in the
- // Android 2.1 browser, where if you click on an element, and there is a
- // mouse/click handler on one of its ancestors, the target will be the
- // innermost child of the touched element, even if that child is no where
- // near the point of touch.
-
- ele = target;
-
- while ( ele ) {
- for ( i = 0; i < cnt; i++ ) {
- o = clickBlockList[ i ];
- touchID = 0;
-
- if ( ( ele === target && Math.abs( o.x - x ) < threshold && Math.abs( o.y - y ) < threshold ) ||
- $.data( ele, touchTargetPropertyName ) === o.touchID ) {
- // XXX: We may want to consider removing matches from the block list
- // instead of waiting for the reset timer to fire.
- e.preventDefault();
- e.stopPropagation();
- return;
- }
- }
- ele = ele.parentNode;
- }
- }
- }, true);
-}
-})( jQuery, window, document );
-/*
-* jQuery Mobile Framework : events
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-(function( $, window, undefined ) {
-
-// add new event shortcuts
-$.each( ( "touchstart touchmove touchend orientationchange throttledresize " +
- "tap taphold swipe swipeleft swiperight scrollstart scrollstop" ).split( " " ), function( i, name ) {
-
- $.fn[ name ] = function( fn ) {
- return fn ? this.bind( name, fn ) : this.trigger( name );
- };
-
- $.attrFn[ name ] = true;
-});
-
-var supportTouch = $.support.touch,
- scrollEvent = "touchmove scroll",
- touchStartEvent = supportTouch ? "touchstart" : "mousedown",
- touchStopEvent = supportTouch ? "touchend" : "mouseup",
- touchMoveEvent = supportTouch ? "touchmove" : "mousemove";
-
-function triggerCustomEvent( obj, eventType, event ) {
- var originalType = event.type;
- event.type = eventType;
- $.event.handle.call( obj, event );
- event.type = originalType;
-}
-
-// also handles scrollstop
-$.event.special.scrollstart = {
-
- enabled: true,
-
- setup: function() {
-
- var thisObject = this,
- $this = $( thisObject ),
- scrolling,
- timer;
-
- function trigger( event, state ) {
- scrolling = state;
- triggerCustomEvent( thisObject, scrolling ? "scrollstart" : "scrollstop", event );
- }
-
- // iPhone triggers scroll after a small delay; use touchmove instead
- $this.bind( scrollEvent, function( event ) {
-
- if ( !$.event.special.scrollstart.enabled ) {
- return;
- }
-
- if ( !scrolling ) {
- trigger( event, true );
- }
-
- clearTimeout( timer );
- timer = setTimeout(function() {
- trigger( event, false );
- }, 50 );
- });
- }
-};
-
-// also handles taphold
-$.event.special.tap = {
- setup: function() {
- var thisObject = this,
- $this = $( thisObject );
-
- $this.bind( "vmousedown", function( event ) {
-
- if ( event.which && event.which !== 1 ) {
- return false;
- }
-
- var origTarget = event.target,
- origEvent = event.originalEvent,
- timer;
-
- function clearTapTimer() {
- clearTimeout( timer );
- }
-
- function clearTapHandlers() {
- clearTapTimer();
-
- $this.unbind( "vclick", clickHandler )
- .unbind( "vmouseup", clearTapTimer )
- .unbind( "vmousecancel", clearTapHandlers );
- }
-
- function clickHandler(event) {
- clearTapHandlers();
-
- // ONLY trigger a 'tap' event if the start target is
- // the same as the stop target.
- if ( origTarget == event.target ) {
- triggerCustomEvent( thisObject, "tap", event );
- }
- }
-
- $this.bind( "vmousecancel", clearTapHandlers )
- .bind( "vmouseup", clearTapTimer )
- .bind( "vclick", clickHandler );
-
- timer = setTimeout(function() {
- triggerCustomEvent( thisObject, "taphold", $.Event( "taphold" ) );
- }, 750 );
- });
- }
-};
-
-// also handles swipeleft, swiperight
-$.event.special.swipe = {
- scrollSupressionThreshold: 10, // More than this horizontal displacement, and we will suppress scrolling.
-
- durationThreshold: 1000, // More time than this, and it isn't a swipe.
-
- horizontalDistanceThreshold: 30, // Swipe horizontal displacement must be more than this.
-
- verticalDistanceThreshold: 75, // Swipe vertical displacement must be less than this.
-
- setup: function() {
- var thisObject = this,
- $this = $( thisObject );
-
- $this.bind( touchStartEvent, function( event ) {
- var data = event.originalEvent.touches ?
- event.originalEvent.touches[ 0 ] : event,
- start = {
- time: ( new Date() ).getTime(),
- coords: [ data.pageX, data.pageY ],
- origin: $( event.target )
- },
- stop;
-
- function moveHandler( event ) {
-
- if ( !start ) {
- return;
- }
-
- var data = event.originalEvent.touches ?
- event.originalEvent.touches[ 0 ] : event;
-
- stop = {
- time: ( new Date() ).getTime(),
- coords: [ data.pageX, data.pageY ]
- };
-
- // prevent scrolling
- if ( Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollSupressionThreshold ) {
- event.preventDefault();
- }
- }
-
- $this.bind( touchMoveEvent, moveHandler )
- .one( touchStopEvent, function( event ) {
- $this.unbind( touchMoveEvent, moveHandler );
-
- if ( start && stop ) {
- if ( stop.time - start.time < $.event.special.swipe.durationThreshold &&
- Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.horizontalDistanceThreshold &&
- Math.abs( start.coords[ 1 ] - stop.coords[ 1 ] ) < $.event.special.swipe.verticalDistanceThreshold ) {
-
- start.origin.trigger( "swipe" )
- .trigger( start.coords[0] > stop.coords[ 0 ] ? "swipeleft" : "swiperight" );
- }
- }
- start = stop = undefined;
- });
- });
- }
-};
-
-(function( $, window ) {
- // "Cowboy" Ben Alman
-
- var win = $( window ),
- special_event,
- get_orientation,
- last_orientation;
-
- $.event.special.orientationchange = special_event = {
- setup: function() {
- // If the event is supported natively, return false so that jQuery
- // will bind to the event using DOM methods.
- if ( $.support.orientation ) {
- return false;
- }
-
- // Get the current orientation to avoid initial double-triggering.
- last_orientation = get_orientation();
-
- // Because the orientationchange event doesn't exist, simulate the
- // event by testing window dimensions on resize.
- win.bind( "throttledresize", handler );
- },
- teardown: function(){
- // If the event is not supported natively, return false so that
- // jQuery will unbind the event using DOM methods.
- if ( $.support.orientation ) {
- return false;
- }
-
- // Because the orientationchange event doesn't exist, unbind the
- // resize event handler.
- win.unbind( "throttledresize", handler );
- },
- add: function( handleObj ) {
- // Save a reference to the bound event handler.
- var old_handler = handleObj.handler;
-
- handleObj.handler = function( event ) {
- // Modify event object, adding the .orientation property.
- event.orientation = get_orientation();
-
- // Call the originally-bound event handler and return its result.
- return old_handler.apply( this, arguments );
- };
- }
- };
-
- // If the event is not supported natively, this handler will be bound to
- // the window resize event to simulate the orientationchange event.
- function handler() {
- // Get the current orientation.
- var orientation = get_orientation();
-
- if ( orientation !== last_orientation ) {
- // The orientation has changed, so trigger the orientationchange event.
- last_orientation = orientation;
- win.trigger( "orientationchange" );
- }
- };
-
- // Get the current page orientation. This method is exposed publicly, should it
- // be needed, as jQuery.event.special.orientationchange.orientation()
- $.event.special.orientationchange.orientation = get_orientation = function() {
- var elem = document.documentElement;
- return elem && elem.clientWidth / elem.clientHeight < 1.1 ? "portrait" : "landscape";
- };
-
-})( jQuery, window );
-
-
-// throttled resize event
-(function() {
-
- $.event.special.throttledresize = {
- setup: function() {
- $( this ).bind( "resize", handler );
- },
- teardown: function(){
- $( this ).unbind( "resize", handler );
- }
- };
-
- var throttle = 250,
- handler = function() {
- curr = ( new Date() ).getTime();
- diff = curr - lastCall;
-
- if ( diff >= throttle ) {
-
- lastCall = curr;
- $( this ).trigger( "throttledresize" );
-
- } else {
-
- if ( heldCall ) {
- clearTimeout( heldCall );
- }
-
- // Promise a held call will still execute
- heldCall = setTimeout( handler, throttle - diff );
- }
- },
- lastCall = 0,
- heldCall,
- curr,
- diff;
-})();
-
-
-$.each({
- scrollstop: "scrollstart",
- taphold: "tap",
- swipeleft: "swipe",
- swiperight: "swipe"
-}, function( event, sourceEvent ) {
-
- $.event.special[ event ] = {
- setup: function() {
- $( this ).bind( sourceEvent, $.noop );
- }
- };
-});
-
-})( jQuery, this );
-/*!
- * jQuery hashchange event - v1.3 - 7/21/2010
- * http://benalman.com/projects/jquery-hashchange-plugin/
- *
- * Copyright (c) 2010 "Cowboy" Ben Alman
- * Dual licensed under the MIT and GPL licenses.
- * http://benalman.com/about/license/
- */
-
-// Script: jQuery hashchange event
-//
-// *Version: 1.3, Last updated: 7/21/2010*
-//
-// Project Home - http://benalman.com/projects/jquery-hashchange-plugin/
-// GitHub - http://github.com/cowboy/jquery-hashchange/
-// Source - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js
-// (Minified) - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (0.8kb gzipped)
-//
-// About: License
-//
-// Copyright (c) 2010 "Cowboy" Ben Alman,
-// Dual licensed under the MIT and GPL licenses.
-// http://benalman.com/about/license/
-//
-// About: Examples
-//
-// These working examples, complete with fully commented code, illustrate a few
-// ways in which this plugin can be used.
-//
-// hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/
-// document.domain - http://benalman.com/code/projects/jquery-hashchange/examples/document_domain/
-//
-// About: Support and Testing
-//
-// Information about what version or versions of jQuery this plugin has been
-// tested with, what browsers it has been tested in, and where the unit tests
-// reside (so you can test it yourself).
-//
-// jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2
-// Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5,
-// Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5.
-// Unit Tests - http://benalman.com/code/projects/jquery-hashchange/unit/
-//
-// About: Known issues
-//
-// While this jQuery hashchange event implementation is quite stable and
-// robust, there are a few unfortunate browser bugs surrounding expected
-// hashchange event-based behaviors, independent of any JavaScript
-// window.onhashchange abstraction. See the following examples for more
-// information:
-//
-// Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/
-// Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/
-// WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/
-// Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/
-//
-// Also note that should a browser natively support the window.onhashchange
-// event, but not report that it does, the fallback polling loop will be used.
-//
-// About: Release History
-//
-// 1.3 - (7/21/2010) Reorganized IE6/7 Iframe code to make it more
-// "removable" for mobile-only development. Added IE6/7 document.title
-// support. Attempted to make Iframe as hidden as possible by using
-// techniques from http://www.paciellogroup.com/blog/?p=604. Added
-// support for the "shortcut" format $(window).hashchange( fn ) and
-// $(window).hashchange() like jQuery provides for built-in events.
-// Renamed jQuery.hashchangeDelay to <jQuery.fn.hashchange.delay> and
-// lowered its default value to 50. Added <jQuery.fn.hashchange.domain>
-// and <jQuery.fn.hashchange.src> properties plus document-domain.html
-// file to address access denied issues when setting document.domain in
-// IE6/7.
-// 1.2 - (2/11/2010) Fixed a bug where coming back to a page using this plugin
-// from a page on another domain would cause an error in Safari 4. Also,
-// IE6/7 Iframe is now inserted after the body (this actually works),
-// which prevents the page from scrolling when the event is first bound.
-// Event can also now be bound before DOM ready, but it won't be usable
-// before then in IE6/7.
-// 1.1 - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug
-// where browser version is incorrectly reported as 8.0, despite
-// inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag.
-// 1.0 - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special
-// window.onhashchange functionality into a separate plugin for users
-// who want just the basic event & back button support, without all the
-// extra awesomeness that BBQ provides. This plugin will be included as
-// part of jQuery BBQ, but also be available separately.
-
-(function($,window,undefined){
- '$:nomunge'; // Used by YUI compressor.
-
- // Reused string.
- var str_hashchange = 'hashchange',
-
- // Method / object references.
- doc = document,
- fake_onhashchange,
- special = $.event.special,
-
- // Does the browser support window.onhashchange? Note that IE8 running in
- // IE7 compatibility mode reports true for 'onhashchange' in window, even
- // though the event isn't supported, so also test document.documentMode.
- doc_mode = doc.documentMode,
- supports_onhashchange = 'on' + str_hashchange in window && ( doc_mode === undefined || doc_mode > 7 );
-
- // Get location.hash (or what you'd expect location.hash to be) sans any
- // leading #. Thanks for making this necessary, Firefox!
- function get_fragment( url ) {
- url = url || location.href;
- return '#' + url.replace( /^[^#]*#?(.*)$/, '$1' );
- };
-
- // Method: jQuery.fn.hashchange
- //
- // Bind a handler to the window.onhashchange event or trigger all bound
- // window.onhashchange event handlers. This behavior is consistent with
- // jQuery's built-in event handlers.
- //
- // Usage:
- //
- // > jQuery(window).hashchange( [ handler ] );
- //
- // Arguments:
- //
- // handler - (Function) Optional handler to be bound to the hashchange
- // event. This is a "shortcut" for the more verbose form:
- // jQuery(window).bind( 'hashchange', handler ). If handler is omitted,
- // all bound window.onhashchange event handlers will be triggered. This
- // is a shortcut for the more verbose
- // jQuery(window).trigger( 'hashchange' ). These forms are described in
- // the <hashchange event> section.
- //
- // Returns:
- //
- // (jQuery) The initial jQuery collection of elements.
-
- // Allow the "shortcut" format $(elem).hashchange( fn ) for binding and
- // $(elem).hashchange() for triggering, like jQuery does for built-in events.
- $.fn[ str_hashchange ] = function( fn ) {
- return fn ? this.bind( str_hashchange, fn ) : this.trigger( str_hashchange );
- };
-
- // Property: jQuery.fn.hashchange.delay
- //
- // The numeric interval (in milliseconds) at which the <hashchange event>
- // polling loop executes. Defaults to 50.
-
- // Property: jQuery.fn.hashchange.domain
- //
- // If you're setting document.domain in your JavaScript, and you want hash
- // history to work in IE6/7, not only must this property be set, but you must
- // also set document.domain BEFORE jQuery is loaded into the page. This
- // property is only applicable if you are supporting IE6/7 (or IE8 operating
- // in "IE7 compatibility" mode).
- //
- // In addition, the <jQuery.fn.hashchange.src> property must be set to the
- // path of the included "document-domain.html" file, which can be renamed or
- // modified if necessary (note that the document.domain specified must be the
- // same in both your main JavaScript as well as in this file).
- //
- // Usage:
- //
- // jQuery.fn.hashchange.domain = document.domain;
-
- // Property: jQuery.fn.hashchange.src
- //
- // If, for some reason, you need to specify an Iframe src file (for example,
- // when setting document.domain as in <jQuery.fn.hashchange.domain>), you can
- // do so using this property. Note that when using this property, history
- // won't be recorded in IE6/7 until the Iframe src file loads. This property
- // is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7
- // compatibility" mode).
- //
- // Usage:
- //
- // jQuery.fn.hashchange.src = 'path/to/file.html';
-
- $.fn[ str_hashchange ].delay = 50;
- /*
- $.fn[ str_hashchange ].domain = null;
- $.fn[ str_hashchange ].src = null;
- */
-
- // Event: hashchange event
- //
- // Fired when location.hash changes. In browsers that support it, the native
- // HTML5 window.onhashchange event is used, otherwise a polling loop is
- // initialized, running every <jQuery.fn.hashchange.delay> milliseconds to
- // see if the hash has changed. In IE6/7 (and IE8 operating in "IE7
- // compatibility" mode), a hidden Iframe is created to allow the back button
- // and hash-based history to work.
- //
- // Usage as described in <jQuery.fn.hashchange>:
- //
- // > // Bind an event handler.
- // > jQuery(window).hashchange( function(e) {
- // > var hash = location.hash;
- // > ...
- // > });
- // >
- // > // Manually trigger the event handler.
- // > jQuery(window).hashchange();
- //
- // A more verbose usage that allows for event namespacing:
- //
- // > // Bind an event handler.
- // > jQuery(window).bind( 'hashchange', function(e) {
- // > var hash = location.hash;
- // > ...
- // > });
- // >
- // > // Manually trigger the event handler.
- // > jQuery(window).trigger( 'hashchange' );
- //
- // Additional Notes:
- //
- // * The polling loop and Iframe are not created until at least one handler
- // is actually bound to the 'hashchange' event.
- // * If you need the bound handler(s) to execute immediately, in cases where
- // a location.hash exists on page load, via bookmark or page refresh for
- // example, use jQuery(window).hashchange() or the more verbose
- // jQuery(window).trigger( 'hashchange' ).
- // * The event can be bound before DOM ready, but since it won't be usable
- // before then in IE6/7 (due to the necessary Iframe), recommended usage is
- // to bind it inside a DOM ready handler.
-
- // Override existing $.event.special.hashchange methods (allowing this plugin
- // to be defined after jQuery BBQ in BBQ's source code).
- special[ str_hashchange ] = $.extend( special[ str_hashchange ], {
-
- // Called only when the first 'hashchange' event is bound to window.
- setup: function() {
- // If window.onhashchange is supported natively, there's nothing to do..
- if ( supports_onhashchange ) { return false; }
-
- // Otherwise, we need to create our own. And we don't want to call this
- // until the user binds to the event, just in case they never do, since it
- // will create a polling loop and possibly even a hidden Iframe.
- $( fake_onhashchange.start );
- },
-
- // Called only when the last 'hashchange' event is unbound from window.
- teardown: function() {
- // If window.onhashchange is supported natively, there's nothing to do..
- if ( supports_onhashchange ) { return false; }
-
- // Otherwise, we need to stop ours (if possible).
- $( fake_onhashchange.stop );
- }
-
- });
-
- // fake_onhashchange does all the work of triggering the window.onhashchange
- // event for browsers that don't natively support it, including creating a
- // polling loop to watch for hash changes and in IE 6/7 creating a hidden
- // Iframe to enable back and forward.
- fake_onhashchange = (function(){
- var self = {},
- timeout_id,
-
- // Remember the initial hash so it doesn't get triggered immediately.
- last_hash = get_fragment(),
-
- fn_retval = function(val){ return val; },
- history_set = fn_retval,
- history_get = fn_retval;
-
- // Start the polling loop.
- self.start = function() {
- timeout_id || poll();
- };
-
- // Stop the polling loop.
- self.stop = function() {
- timeout_id && clearTimeout( timeout_id );
- timeout_id = undefined;
- };
-
- // This polling loop checks every $.fn.hashchange.delay milliseconds to see
- // if location.hash has changed, and triggers the 'hashchange' event on
- // window when necessary.
- function poll() {
- var hash = get_fragment(),
- history_hash = history_get( last_hash );
-
- if ( hash !== last_hash ) {
- history_set( last_hash = hash, history_hash );
-
- $(window).trigger( str_hashchange );
-
- } else if ( history_hash !== last_hash ) {
- location.href = location.href.replace( /#.*/, '' ) + history_hash;
- }
-
- timeout_id = setTimeout( poll, $.fn[ str_hashchange ].delay );
- };
-
- // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
- // vvvvvvvvvvvvvvvvvvv REMOVE IF NOT SUPPORTING IE6/7/8 vvvvvvvvvvvvvvvvvvv
- // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
- $.browser.msie && !supports_onhashchange && (function(){
- // Not only do IE6/7 need the "magical" Iframe treatment, but so does IE8
- // when running in "IE7 compatibility" mode.
-
- var iframe,
- iframe_src;
-
- // When the event is bound and polling starts in IE 6/7, create a hidden
- // Iframe for history handling.
- self.start = function(){
- if ( !iframe ) {
- iframe_src = $.fn[ str_hashchange ].src;
- iframe_src = iframe_src && iframe_src + get_fragment();
-
- // Create hidden Iframe. Attempt to make Iframe as hidden as possible
- // by using techniques from http://www.paciellogroup.com/blog/?p=604.
- iframe = $('<iframe tabindex="-1" title="empty"/>').hide()
-
- // When Iframe has completely loaded, initialize the history and
- // start polling.
- .one( 'load', function(){
- iframe_src || history_set( get_fragment() );
- poll();
- })
-
- // Load Iframe src if specified, otherwise nothing.
- .attr( 'src', iframe_src || 'javascript:0' )
-
- // Append Iframe after the end of the body to prevent unnecessary
- // initial page scrolling (yes, this works).
- .insertAfter( 'body' )[0].contentWindow;
-
- // Whenever `document.title` changes, update the Iframe's title to
- // prettify the back/next history menu entries. Since IE sometimes
- // errors with "Unspecified error" the very first time this is set
- // (yes, very useful) wrap this with a try/catch block.
- doc.onpropertychange = function(){
- try {
- if ( event.propertyName === 'title' ) {
- iframe.document.title = doc.title;
- }
- } catch(e) {}
- };
-
- }
- };
-
- // Override the "stop" method since an IE6/7 Iframe was created. Even
- // if there are no longer any bound event handlers, the polling loop
- // is still necessary for back/next to work at all!
- self.stop = fn_retval;
-
- // Get history by looking at the hidden Iframe's location.hash.
- history_get = function() {
- return get_fragment( iframe.location.href );
- };
-
- // Set a new history item by opening and then closing the Iframe
- // document, *then* setting its location.hash. If document.domain has
- // been set, update that as well.
- history_set = function( hash, history_hash ) {
- var iframe_doc = iframe.document,
- domain = $.fn[ str_hashchange ].domain;
-
- if ( hash !== history_hash ) {
- // Update Iframe with any initial `document.title` that might be set.
- iframe_doc.title = doc.title;
-
- // Opening the Iframe's document after it has been closed is what
- // actually adds a history entry.
- iframe_doc.open();
-
- // Set document.domain for the Iframe document as well, if necessary.
- domain && iframe_doc.write( '<script>document.domain="' + domain + '"</script>' );
-
- iframe_doc.close();
-
- // Update the Iframe's hash, for great justice.
- iframe.location.hash = hash;
- }
- };
-
- })();
- // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- // ^^^^^^^^^^^^^^^^^^^ REMOVE IF NOT SUPPORTING IE6/7/8 ^^^^^^^^^^^^^^^^^^^
- // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
- return self;
- })();
-
-})(jQuery,this);
-/*
-* jQuery Mobile Framework : "page" plugin
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-
-(function( $, undefined ) {
-
-$.widget( "mobile.page", $.mobile.widget, {
- options: {
- theme: "c",
- domCache: false
- },
-
- _create: function() {
-
- this._trigger( "beforecreate" );
-
- this.element
- .attr( "tabindex", "0" )
- .addClass( "ui-page ui-body-" + this.options.theme );
- }
-});
-
-})( jQuery );
-/*!
- * jQuery Mobile v@VERSION
- * http://jquerymobile.com/
- *
- * Copyright 2010, jQuery Project
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- */
-
-(function( $, window, undefined ) {
-
- // jQuery.mobile configurable options
- $.extend( $.mobile, {
-
- // Namespace used framework-wide for data-attrs. Default is no namespace
- ns: "",
-
- // Define the url parameter used for referencing widget-generated sub-pages.
- // Translates to to example.html&ui-page=subpageIdentifier
- // hash segment before &ui-page= is used to make Ajax request
- subPageUrlKey: "ui-page",
-
- // Class assigned to page currently in view, and during transitions
- activePageClass: "ui-page-active",
-
- // Class used for "active" button state, from CSS framework
- activeBtnClass: "ui-btn-active",
-
- // Automatically handle clicks and form submissions through Ajax, when same-domain
- ajaxEnabled: true,
-
- // Automatically load and show pages based on location.hash
- hashListeningEnabled: true,
-
- // Set default page transition - 'none' for no transitions
- defaultPageTransition: "slide",
-
- // Minimum scroll distance that will be remembered when returning to a page
- minScrollBack: 250,
-
- // Set default dialog transition - 'none' for no transitions
- defaultDialogTransition: "pop",
-
- // Show loading message during Ajax requests
- // if false, message will not appear, but loading classes will still be toggled on html el
- loadingMessage: "loading",
-
- // Error response message - appears when an Ajax page request fails
- pageLoadErrorMessage: "Error Loading Page",
-
- //automatically initialize the DOM when it's ready
- autoInitializePage: true,
-
- pushStateEnabled: true,
-
- // Support conditions that must be met in order to proceed
- // default enhanced qualifications are media query support OR IE 7+
- gradeA: function(){
- return $.support.mediaquery || $.mobile.browser.ie && $.mobile.browser.ie >= 7;
- },
-
- // TODO might be useful upstream in jquery itself ?
- keyCode: {
- ALT: 18,
- BACKSPACE: 8,
- CAPS_LOCK: 20,
- COMMA: 188,
- COMMAND: 91,
- COMMAND_LEFT: 91, // COMMAND
- COMMAND_RIGHT: 93,
- CONTROL: 17,
- DELETE: 46,
- DOWN: 40,
- END: 35,
- ENTER: 13,
- ESCAPE: 27,
- HOME: 36,
- INSERT: 45,
- LEFT: 37,
- MENU: 93, // COMMAND_RIGHT
- NUMPAD_ADD: 107,
- NUMPAD_DECIMAL: 110,
- NUMPAD_DIVIDE: 111,
- NUMPAD_ENTER: 108,
- NUMPAD_MULTIPLY: 106,
- NUMPAD_SUBTRACT: 109,
- PAGE_DOWN: 34,
- PAGE_UP: 33,
- PERIOD: 190,
- RIGHT: 39,
- SHIFT: 16,
- SPACE: 32,
- TAB: 9,
- UP: 38,
- WINDOWS: 91 // COMMAND
- },
-
- // Scroll page vertically: scroll to 0 to hide iOS address bar, or pass a Y value
- silentScroll: function( ypos ) {
- if ( $.type( ypos ) !== "number" ) {
- ypos = $.mobile.defaultHomeScroll;
- }
-
- // prevent scrollstart and scrollstop events
- $.event.special.scrollstart.enabled = false;
-
- setTimeout(function() {
- window.scrollTo( 0, ypos );
- $( document ).trigger( "silentscroll", { x: 0, y: ypos });
- }, 20 );
-
- setTimeout(function() {
- $.event.special.scrollstart.enabled = true;
- }, 150 );
- },
-
- // Take a data attribute property, prepend the namespace
- // and then camel case the attribute string
- nsNormalize: function( prop ) {
- if ( !prop ) {
- return;
- }
-
- return $.camelCase( $.mobile.ns + prop );
- }
- });
-
- // Mobile version of data and removeData and hasData methods
- // ensures all data is set and retrieved using jQuery Mobile's data namespace
- $.fn.jqmData = function( prop, value ) {
- return this.data( prop ? $.mobile.nsNormalize( prop ) : prop, value );
- };
-
- $.jqmData = function( elem, prop, value ) {
- return $.data( elem, $.mobile.nsNormalize( prop ), value );
- };
-
- $.fn.jqmRemoveData = function( prop ) {
- return this.removeData( $.mobile.nsNormalize( prop ) );
- };
-
- $.jqmRemoveData = function( elem, prop ) {
- return $.removeData( elem, $.mobile.nsNormalize( prop ) );
- };
-
- $.jqmHasData = function( elem, prop ) {
- return $.hasData( elem, $.mobile.nsNormalize( prop ) );
- };
-
- // Monkey-patching Sizzle to filter the :jqmData selector
- var oldFind = $.find;
-
- $.find = function( selector, context, ret, extra ) {
- selector = selector.replace(/:jqmData\(([^)]*)\)/g, "[data-" + ( $.mobile.ns || "" ) + "$1]");
-
- return oldFind.call( this, selector, context, ret, extra );
- };
-
- $.extend( $.find, oldFind );
-
- $.find.matches = function( expr, set ) {
- return $.find( expr, null, null, set );
- };
-
- $.find.matchesSelector = function( node, expr ) {
- return $.find( expr, null, null, [ node ] ).length > 0;
- };
-})( jQuery, this );
-
-/*
-* jQuery Mobile Framework : core utilities for auto ajax navigation, base tag mgmt,
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-( function( $, undefined ) {
-
- //define vars for interal use
- var $window = $( window ),
- $html = $( 'html' ),
- $head = $( 'head' ),
-
- //url path helpers for use in relative url management
- path = {
-
- // This scary looking regular expression parses an absolute URL or its relative
- // variants (protocol, site, document, query, and hash), into the various
- // components (protocol, host, path, query, fragment, etc that make up the
- // URL as well as some other commonly used sub-parts. When used with RegExp.exec()
- // or String.match, it parses the URL into a results array that looks like this:
- //
- // [0]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread#msg-content
- // [1]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread
- // [2]: http://jblas:password@mycompany.com:8080/mail/inbox
- // [3]: http://jblas:password@mycompany.com:8080
- // [4]: http:
- // [5]: jblas:password@mycompany.com:8080
- // [6]: jblas:password
- // [7]: jblas
- // [8]: password
- // [9]: mycompany.com:8080
- // [10]: mycompany.com
- // [11]: 8080
- // [12]: /mail/inbox
- // [13]: /mail/
- // [14]: inbox
- // [15]: ?msg=1234&type=unread
- // [16]: #msg-content
- //
- urlParseRE: /^(((([^:\/#\?]+:)?(?:\/\/((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,
-
- //Parse a URL into a structure that allows easy access to
- //all of the URL components by name.
- parseUrl: function( url ) {
- // If we're passed an object, we'll assume that it is
- // a parsed url object and just return it back to the caller.
- if ( $.type( url ) === "object" ) {
- return url;
- }
-
- var u = url || "",
- matches = path.urlParseRE.exec( url ),
- results;
- if ( matches ) {
- // Create an object that allows the caller to access the sub-matches
- // by name. Note that IE returns an empty string instead of undefined,
- // like all other browsers do, so we normalize everything so its consistent
- // no matter what browser we're running on.
- results = {
- href: matches[0] || "",
- hrefNoHash: matches[1] || "",
- hrefNoSearch: matches[2] || "",
- domain: matches[3] || "",
- protocol: matches[4] || "",
- authority: matches[5] || "",
- username: matches[7] || "",
- password: matches[8] || "",
- host: matches[9] || "",
- hostname: matches[10] || "",
- port: matches[11] || "",
- pathname: matches[12] || "",
- directory: matches[13] || "",
- filename: matches[14] || "",
- search: matches[15] || "",
- hash: matches[16] || ""
- };
- }
- return results || {};
- },
-
- //Turn relPath into an asbolute path. absPath is
- //an optional absolute path which describes what
- //relPath is relative to.
- makePathAbsolute: function( relPath, absPath ) {
- if ( relPath && relPath.charAt( 0 ) === "/" ) {
- return relPath;
- }
-
- relPath = relPath || "";
- absPath = absPath ? absPath.replace( /^\/|(\/[^\/]*|[^\/]+)$/g, "" ) : "";
-
- var absStack = absPath ? absPath.split( "/" ) : [],
- relStack = relPath.split( "/" );
- for ( var i = 0; i < relStack.length; i++ ) {
- var d = relStack[ i ];
- switch ( d ) {
- case ".":
- break;
- case "..":
- if ( absStack.length ) {
- absStack.pop();
- }
- break;
- default:
- absStack.push( d );
- break;
- }
- }
- return "/" + absStack.join( "/" );
- },
-
- //Returns true if both urls have the same domain.
- isSameDomain: function( absUrl1, absUrl2 ) {
- return path.parseUrl( absUrl1 ).domain === path.parseUrl( absUrl2 ).domain;
- },
-
- //Returns true for any relative variant.
- isRelativeUrl: function( url ) {
- // All relative Url variants have one thing in common, no protocol.
- return path.parseUrl( url ).protocol === "";
- },
-
- //Returns true for an absolute url.
- isAbsoluteUrl: function( url ) {
- return path.parseUrl( url ).protocol !== "";
- },
-
- //Turn the specified realtive URL into an absolute one. This function
- //can handle all relative variants (protocol, site, document, query, fragment).
- makeUrlAbsolute: function( relUrl, absUrl ) {
- if ( !path.isRelativeUrl( relUrl ) ) {
- return relUrl;
- }
-
- var relObj = path.parseUrl( relUrl ),
- absObj = path.parseUrl( absUrl ),
- protocol = relObj.protocol || absObj.protocol,
- authority = relObj.authority || absObj.authority,
- hasPath = relObj.pathname !== "",
- pathname = path.makePathAbsolute( relObj.pathname || absObj.filename, absObj.pathname ),
- search = relObj.search || ( !hasPath && absObj.search ) || "",
- hash = relObj.hash;
-
- return protocol + "//" + authority + pathname + search + hash;
- },
-
- //Add search (aka query) params to the specified url.
- addSearchParams: function( url, params ) {
- var u = path.parseUrl( url ),
- p = ( typeof params === "object" ) ? $.param( params ) : params,
- s = u.search || "?";
- return u.hrefNoSearch + s + ( s.charAt( s.length - 1 ) !== "?" ? "&" : "" ) + p + ( u.hash || "" );
- },
-
- convertUrlToDataUrl: function( absUrl ) {
- var u = path.parseUrl( absUrl );
- if ( path.isEmbeddedPage( u ) ) {
- // For embedded pages, remove the dialog hash key as in getFilePath(),
- // otherwise the Data Url won't match the id of the embedded Page.
- return u.hash.split( dialogHashKey )[0].replace( /^#/, "" );
- } else if ( path.isSameDomain( u, documentBase ) ) {
- return u.hrefNoHash.replace( documentBase.domain, "" );
- }
- return absUrl;
- },
-
- //get path from current hash, or from a file path
- get: function( newPath ) {
- if( newPath === undefined ) {
- newPath = location.hash;
- }
- return path.stripHash( newPath ).replace( /[^\/]*\.[^\/*]+$/, '' );
- },
-
- //return the substring of a filepath before the sub-page key, for making a server request
- getFilePath: function( path ) {
- var splitkey = '&' + $.mobile.subPageUrlKey;
- return path && path.split( splitkey )[0].split( dialogHashKey )[0];
- },
-
- //set location hash to path
- set: function( path ) {
- location.hash = path;
- },
-
- //test if a given url (string) is a path
- //NOTE might be exceptionally naive
- isPath: function( url ) {
- return ( /\// ).test( url );
- },
-
- //return a url path with the window's location protocol/hostname/pathname removed
- clean: function( url ) {
- return url.replace( documentBase.domain, "" );
- },
-
- //just return the url without an initial #
- stripHash: function( url ) {
- return url.replace( /^#/, "" );
- },
-
- //remove the preceding hash, any query params, and dialog notations
- cleanHash: function( hash ) {
- return path.stripHash( hash.replace( /\?.*$/, "" ).replace( dialogHashKey, "" ) );
- },
-
- //check whether a url is referencing the same domain, or an external domain or different protocol
- //could be mailto, etc
- isExternal: function( url ) {
- var u = path.parseUrl( url );
- return u.protocol && u.domain !== documentUrl.domain ? true : false;
- },
-
- hasProtocol: function( url ) {
- return ( /^(:?\w+:)/ ).test( url );
- },
-
- //check if the specified url refers to the first page in the main application document.
- isFirstPageUrl: function( url ) {
- // We only deal with absolute paths.
- var u = path.parseUrl( path.makeUrlAbsolute( url, documentBase ) ),
-
- // Does the url have the same path as the document?
- samePath = u.hrefNoHash === documentUrl.hrefNoHash || ( documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash ),
-
- // Get the first page element.
- fp = $.mobile.firstPage,
-
- // Get the id of the first page element if it has one.
- fpId = fp && fp[0] ? fp[0].id : undefined;
-
- // The url refers to the first page if the path matches the document and
- // it either has no hash value, or the hash is exactly equal to the id of the
- // first page element.
- return samePath && ( !u.hash || u.hash === "#" || ( fpId && u.hash.replace( /^#/, "" ) === fpId ) );
- },
-
- isEmbeddedPage: function( url ) {
- var u = path.parseUrl( url );
-
- //if the path is absolute, then we need to compare the url against
- //both the documentUrl and the documentBase. The main reason for this
- //is that links embedded within external documents will refer to the
- //application document, whereas links embedded within the application
- //document will be resolved against the document base.
- if ( u.protocol !== "" ) {
- return ( u.hash && ( u.hrefNoHash === documentUrl.hrefNoHash || ( documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash ) ) );
- }
- return (/^#/).test( u.href );
- }
- },
-
- //will be defined when a link is clicked and given an active class
- $activeClickedLink = null,
-
- //urlHistory is purely here to make guesses at whether the back or forward button was clicked
- //and provide an appropriate transition
- urlHistory = {
- // Array of pages that are visited during a single page load.
- // Each has a url and optional transition, title, and pageUrl (which represents the file path, in cases where URL is obscured, such as dialogs)
- stack: [],
-
- //maintain an index number for the active page in the stack
- activeIndex: 0,
-
- //get active
- getActive: function() {
- return urlHistory.stack[ urlHistory.activeIndex ];
- },
-
- getPrev: function() {
- return urlHistory.stack[ urlHistory.activeIndex - 1 ];
- },
-
- getNext: function() {
- return urlHistory.stack[ urlHistory.activeIndex + 1 ];
- },
-
- // addNew is used whenever a new page is added
- addNew: function( url, transition, title, pageUrl, role ) {
- //if there's forward history, wipe it
- if( urlHistory.getNext() ) {
- urlHistory.clearForward();
- }
-
- urlHistory.stack.push( {url : url, transition: transition, title: title, pageUrl: pageUrl, role: role } );
-
- urlHistory.activeIndex = urlHistory.stack.length - 1;
- },
-
- //wipe urls ahead of active index
- clearForward: function() {
- urlHistory.stack = urlHistory.stack.slice( 0, urlHistory.activeIndex + 1 );
- },
-
- directHashChange: function( opts ) {
- var back , forward, newActiveIndex, prev = this.getActive();
-
- // check if url isp in history and if it's ahead or behind current page
- $.each( urlHistory.stack, function( i, historyEntry ) {
-
- //if the url is in the stack, it's a forward or a back
- if( opts.currentUrl === historyEntry.url ) {
- //define back and forward by whether url is older or newer than current page
- back = i < urlHistory.activeIndex;
- forward = !back;
- newActiveIndex = i;
- }
- });
-
- // save new page index, null check to prevent falsey 0 result
- this.activeIndex = newActiveIndex !== undefined ? newActiveIndex : this.activeIndex;
-
- if( back ) {
- ( opts.either || opts.isBack )( true );
- } else if( forward ) {
- ( opts.either || opts.isForward )( false );
- }
- },
-
- //disable hashchange event listener internally to ignore one change
- //toggled internally when location.hash is updated to match the url of a successful page load
- ignoreNextHashChange: false
- },
-
- //define first selector to receive focus when a page is shown
- focusable = "[tabindex],a,button:visible,select:visible,input",
-
- //queue to hold simultanious page transitions
- pageTransitionQueue = [],
-
- //indicates whether or not page is in process of transitioning
- isPageTransitioning = false,
-
- //nonsense hash change key for dialogs, so they create a history entry
- dialogHashKey = "&ui-state=dialog",
-
- //existing base tag?
- $base = $head.children( "base" ),
-
- //tuck away the original document URL minus any fragment.
- documentUrl = path.parseUrl( location.href ),
-
- //if the document has an embedded base tag, documentBase is set to its
- //initial value. If a base tag does not exist, then we default to the documentUrl.
- documentBase = $base.length ? path.parseUrl( path.makeUrlAbsolute( $base.attr( "href" ), documentUrl.href ) ) : documentUrl,
-
- //cache the comparison once.
- documentBaseDiffers = ( documentUrl.hrefNoHash !== documentBase.hrefNoHash );
-
- //base element management, defined depending on dynamic base tag support
- var base = $.support.dynamicBaseTag ? {
-
- //define base element, for use in routing asset urls that are referenced in Ajax-requested markup
- element: ( $base.length ? $base : $( "<base>", { href: documentBase.hrefNoHash } ).prependTo( $head ) ),
-
- //set the generated BASE element's href attribute to a new page's base path
- set: function( href ) {
- base.element.attr( "href", path.makeUrlAbsolute( href, documentBase ) );
- },
-
- //set the generated BASE element's href attribute to a new page's base path
- reset: function() {
- base.element.attr( "href", documentBase.hrefNoHash );
- }
-
- } : undefined;
-
-/*
- internal utility functions
---------------------------------------*/
-
-
- //direct focus to the page title, or otherwise first focusable element
- function reFocus( page ) {
- var pageTitle = page.find( ".ui-title:eq(0)" );
-
- if( pageTitle.length ) {
- pageTitle.focus();
- }
- else{
- page.focus();
- }
- }
-
- //remove active classes after page transition or error
- function removeActiveLinkClass( forceRemoval ) {
- if( !!$activeClickedLink && ( !$activeClickedLink.closest( '.ui-page-active' ).length || forceRemoval ) ) {
- $activeClickedLink.removeClass( $.mobile.activeBtnClass );
- }
- $activeClickedLink = null;
- }
-
- function releasePageTransitionLock() {
- isPageTransitioning = false;
- if( pageTransitionQueue.length > 0 ) {
- $.mobile.changePage.apply( null, pageTransitionQueue.pop() );
- }
- }
-
- // Save the last scroll distance per page, before it is hidden
- var getLastScroll = (function( lastScrollEnabled ){
- return function(){
- if( !lastScrollEnabled ){
- lastScrollEnabled = true;
- return;
- }
-
- lastScrollEnabled = false;
-
- var active = $.mobile.urlHistory.getActive(),
- activePage = $( ".ui-page-active" ),
- scrollElem = $( window ),
- touchOverflow = $.support.touchOverflow && $.mobile.touchOverflowEnabled;
-
- if( touchOverflow ){
- scrollElem = activePage.is( ".ui-native-fixed" ) ? activePage.find( ".ui-content" ) : activePage;
- }
-
- if( active ){
- var lastScroll = scrollElem.scrollTop();
-
- // Set active page's lastScroll prop.
- // If the Y location we're scrolling to is less than minScrollBack, let it go.
- active.lastScroll = lastScroll < $.mobile.minScrollBack ? $.mobile.defaultHomeScroll : lastScroll;
- }
- };
- })( true );
-
- // to get last scroll, we need to get scrolltop before the page change
- // using beforechangepage or popstate/hashchange (whichever comes first)
- $( document ).bind( "beforechangepage", getLastScroll );
- $( window ).bind( $.support.pushState ? "popstate" : "hashchange", getLastScroll );
-
- // Make the iOS clock quick-scroll work again if we're using native overflow scrolling
- /*
- if( $.support.touchOverflow ){
- if( $.mobile.touchOverflowEnabled ){
- $( window ).bind( "scrollstop", function(){
- if( $( this ).scrollTop() === 0 ){
- $.mobile.activePage.scrollTop( 0 );
- }
- });
- }
- }
- */
-
- //function for transitioning between two existing pages
- function transitionPages( toPage, fromPage, transition, reverse ) {
-
- //get current scroll distance
- var active = $.mobile.urlHistory.getActive(),
- touchOverflow = $.support.touchOverflow && $.mobile.touchOverflowEnabled,
- toScroll = active.lastScroll || ( touchOverflow ? 0 : $.mobile.defaultHomeScroll ),
- screenHeight = getScreenHeight();
-
- // Scroll to top, hide addr bar
- window.scrollTo( 0, $.mobile.defaultHomeScroll );
-
- if( fromPage ) {
- //trigger before show/hide events
- fromPage.data( "page" )._trigger( "beforehide", null, { nextPage: toPage } );
- }
-
- if( !touchOverflow){
- toPage.height( screenHeight + toScroll );
- }
-
- toPage.data( "page" )._trigger( "beforeshow", null, { prevPage: fromPage || $( "" ) } );
-
- //clear page loader
- $.mobile.hidePageLoadingMsg();
-
- if( touchOverflow && toScroll ){
-
- toPage.addClass( "ui-mobile-pre-transition" );
- // Send focus to page as it is now display: block
- reFocus( toPage );
-
- //set page's scrollTop to remembered distance
- if( toPage.is( ".ui-native-fixed" ) ){
- toPage.find( ".ui-content" ).scrollTop( toScroll );
- }
- else{
- toPage.scrollTop( toScroll );
- }
- }
-
- //find the transition handler for the specified transition. If there
- //isn't one in our transitionHandlers dictionary, use the default one.
- //call the handler immediately to kick-off the transition.
- var th = $.mobile.transitionHandlers[transition || "none"] || $.mobile.defaultTransitionHandler,
- promise = th( transition, reverse, toPage, fromPage );
-
- promise.done(function() {
- //reset toPage height back
- if( !touchOverflow ){
- toPage.height( "" );
- // Send focus to the newly shown page
- reFocus( toPage );
- }
-
- // Jump to top or prev scroll, sometimes on iOS the page has not rendered yet.
- if( !touchOverflow ){
- $.mobile.silentScroll( toScroll );
- }
-
- //trigger show/hide events
- if( fromPage ) {
- if( !touchOverflow ){
- fromPage.height( "" );
- }
-
- fromPage.data( "page" )._trigger( "hide", null, { nextPage: toPage } );
- }
-
- //trigger pageshow, define prevPage as either fromPage or empty jQuery obj
- toPage.data( "page" )._trigger( "show", null, { prevPage: fromPage || $( "" ) } );
- });
-
- return promise;
- }
-
- //simply set the active page's minimum height to screen height, depending on orientation
- function getScreenHeight(){
- var orientation = jQuery.event.special.orientationchange.orientation(),
- port = orientation === "portrait",
- winMin = port ? 480 : 320,
- screenHeight = port ? screen.availHeight : screen.availWidth,
- winHeight = Math.max( winMin, $( window ).height() ),
- pageMin = Math.min( screenHeight, winHeight );
-
- return pageMin;
- }
-
- $.mobile.getScreenHeight = getScreenHeight;
-
- //simply set the active page's minimum height to screen height, depending on orientation
- function resetActivePageHeight(){
- $( "." + $.mobile.activePageClass ).css( "min-height", getScreenHeight() );
- }
-
- //shared page enhancements
- function enhancePage( $page, role ) {
- // If a role was specified, make sure the data-role attribute
- // on the page element is in sync.
- if( role ) {
- $page.attr( "data-" + $.mobile.ns + "role", role );
- }
-
- //run page plugin
- $page.page();
- }
-
-/* exposed $.mobile methods */
-
- //animation complete callback
- $.fn.animationComplete = function( callback ) {
- if( $.support.cssTransitions ) {
- return $( this ).one( 'webkitAnimationEnd', callback );
- }
- else{
- // defer execution for consistency between webkit/non webkit
- setTimeout( callback, 0 );
- return $( this );
- }
- };
-
- //update location.hash, with or without triggering hashchange event
- //TODO - deprecate this one at 1.0
- $.mobile.updateHash = path.set;
-
- //expose path object on $.mobile
- $.mobile.path = path;
-
- //expose base object on $.mobile
- $.mobile.base = base;
-
- //url stack, useful when plugins need to be aware of previous pages viewed
- //TODO: deprecate this one at 1.0
- $.mobile.urlstack = urlHistory.stack;
-
- //history stack
- $.mobile.urlHistory = urlHistory;
-
- $.mobile.dialogHashKey = dialogHashKey;
-
- //default non-animation transition handler
- $.mobile.noneTransitionHandler = function( name, reverse, $toPage, $fromPage ) {
- if ( $fromPage ) {
- $fromPage.removeClass( $.mobile.activePageClass );
- }
- $toPage.addClass( $.mobile.activePageClass );
-
- return $.Deferred().resolve( name, reverse, $toPage, $fromPage ).promise();
- };
-
- //default handler for unknown transitions
- $.mobile.defaultTransitionHandler = $.mobile.noneTransitionHandler;
-
- //transition handler dictionary for 3rd party transitions
- $.mobile.transitionHandlers = {
- none: $.mobile.defaultTransitionHandler
- };
-
- //enable cross-domain page support
- $.mobile.allowCrossDomainPages = false;
-
- //return the original document url
- $.mobile.getDocumentUrl = function(asParsedObject) {
- return asParsedObject ? $.extend( {}, documentUrl ) : documentUrl.href;
- };
-
- //return the original document base url
- $.mobile.getDocumentBase = function(asParsedObject) {
- return asParsedObject ? $.extend( {}, documentBase ) : documentBase.href;
- };
-
- // Load a page into the DOM.
- $.mobile.loadPage = function( url, options ) {
- // This function uses deferred notifications to let callers
- // know when the page is done loading, or if an error has occurred.
- var deferred = $.Deferred(),
-
- // The default loadPage options with overrides specified by
- // the caller.
- settings = $.extend( {}, $.mobile.loadPage.defaults, options ),
-
- // The DOM element for the page after it has been loaded.
- page = null,
-
- // If the reloadPage option is true, and the page is already
- // in the DOM, dupCachedPage will be set to the page element
- // so that it can be removed after the new version of the
- // page is loaded off the network.
- dupCachedPage = null,
-
- // determine the current base url
- findBaseWithDefault = function(){
- var closestBase = ( $.mobile.activePage && getClosestBaseUrl( $.mobile.activePage ) );
- return closestBase || documentBase.hrefNoHash;
- },
-
- // The absolute version of the URL passed into the function. This
- // version of the URL may contain dialog/subpage params in it.
- absUrl = path.makeUrlAbsolute( url, findBaseWithDefault() );
-
-
- // If the caller provided data, and we're using "get" request,
- // append the data to the URL.
- if ( settings.data && settings.type === "get" ) {
- absUrl = path.addSearchParams( absUrl, settings.data );
- settings.data = undefined;
- }
-
- // The absolute version of the URL minus any dialog/subpage params.
- // In otherwords the real URL of the page to be loaded.
- var fileUrl = path.getFilePath( absUrl ),
-
- // The version of the Url actually stored in the data-url attribute of
- // the page. For embedded pages, it is just the id of the page. For pages
- // within the same domain as the document base, it is the site relative
- // path. For cross-domain pages (Phone Gap only) the entire absolute Url
- // used to load the page.
- dataUrl = path.convertUrlToDataUrl( absUrl );
-
- // Make sure we have a pageContainer to work with.
- settings.pageContainer = settings.pageContainer || $.mobile.pageContainer;
-
- // Check to see if the page already exists in the DOM.
- page = settings.pageContainer.children( ":jqmData(url='" + dataUrl + "')" );
-
- // If we failed to find a page in the DOM, check the URL to see if it
- // refers to the first page in the application.
- if ( page.length === 0 && $.mobile.firstPage && path.isFirstPageUrl( absUrl ) ) {
- page = $( $.mobile.firstPage );
- }
-
- // Reset base to the default document base.
- if ( base ) {
- base.reset();
- }
-
- // If the page we are interested in is already in the DOM,
- // and the caller did not indicate that we should force a
- // reload of the file, we are done. Otherwise, track the
- // existing page as a duplicated.
- if ( page.length ) {
- if ( !settings.reloadPage ) {
- enhancePage( page, settings.role );
- deferred.resolve( absUrl, options, page );
- return deferred.promise();
- }
- dupCachedPage = page;
- }
-
- if ( settings.showLoadMsg ) {
-
- // This configurable timeout allows cached pages a brief delay to load without showing a message
- var loadMsgDelay = setTimeout(function(){
- $.mobile.showPageLoadingMsg();
- }, settings.loadMsgDelay ),
-
- // Shared logic for clearing timeout and removing message.
- hideMsg = function(){
-
- // Stop message show timer
- clearTimeout( loadMsgDelay );
-
- // Hide loading message
- $.mobile.hidePageLoadingMsg();
- };
- }
-
- if ( !( $.mobile.allowCrossDomainPages || path.isSameDomain( documentUrl, absUrl ) ) ) {
- deferred.reject( absUrl, options );
- } else {
- // Load the new page.
- $.ajax({
- url: fileUrl,
- type: settings.type,
- data: settings.data,
- dataType: "html",
- success: function( html ) {
- //pre-parse html to check for a data-url,
- //use it as the new fileUrl, base path, etc
- var all = $( "<div></div>" ),
-
- //page title regexp
- newPageTitle = html.match( /<title[^>]*>([^<]*)/ ) && RegExp.$1,
-
- // TODO handle dialogs again
- pageElemRegex = new RegExp( ".*(<[^>]+\\bdata-" + $.mobile.ns + "role=[\"']?page[\"']?[^>]*>).*" ),
- dataUrlRegex = new RegExp( "\\bdata-" + $.mobile.ns + "url=[\"']?([^\"'>]*)[\"']?" );
-
-
- // data-url must be provided for the base tag so resource requests can be directed to the
- // correct url. loading into a temprorary element makes these requests immediately
- if( pageElemRegex.test( html )
- && RegExp.$1
- && dataUrlRegex.test( RegExp.$1 )
- && RegExp.$1 ) {
- url = fileUrl = path.getFilePath( RegExp.$1 );
- }
- else{
-
- }
-
- if ( base ) {
- base.set( fileUrl );
- }
-
- //workaround to allow scripts to execute when included in page divs
- all.get( 0 ).innerHTML = html;
- page = all.find( ":jqmData(role='page'), :jqmData(role='dialog')" ).first();
-
- //if page elem couldn't be found, create one and insert the body element's contents
- if( !page.length ){
- page = $( "<div data-" + $.mobile.ns + "role='page'>" + html.split( /<\/?body[^>]*>/gmi )[1] + "</div>" );
- }
-
- if ( newPageTitle && !page.jqmData( "title" ) ) {
- page.jqmData( "title", newPageTitle );
- }
-
- //rewrite src and href attrs to use a base url
- if( !$.support.dynamicBaseTag ) {
- var newPath = path.get( fileUrl );
- page.find( "[src], link[href], a[rel='external'], :jqmData(ajax='false'), a[target]" ).each(function() {
- var thisAttr = $( this ).is( '[href]' ) ? 'href' :
- $(this).is('[src]') ? 'src' : 'action',
- thisUrl = $( this ).attr( thisAttr );
-
- // XXX_jblas: We need to fix this so that it removes the document
- // base URL, and then prepends with the new page URL.
- //if full path exists and is same, chop it - helps IE out
- thisUrl = thisUrl.replace( location.protocol + '//' + location.host + location.pathname, '' );
-
- if( !/^(\w+:|#|\/)/.test( thisUrl ) ) {
- $( this ).attr( thisAttr, newPath + thisUrl );
- }
- });
- }
-
- //append to page and enhance
- page
- .attr( "data-" + $.mobile.ns + "url", path.convertUrlToDataUrl( fileUrl ) )
- .appendTo( settings.pageContainer );
-
- // wait for page creation to leverage options defined on widget
- page.one('pagecreate', function(){
-
- // when dom caching is not enabled bind to remove the page on hide
- if( !page.data("page").options.domCache ){
- page.bind( "pagehide.remove", function(){
- $(this).remove();
- });
- }
- });
-
- enhancePage( page, settings.role );
-
- // Enhancing the page may result in new dialogs/sub pages being inserted
- // into the DOM. If the original absUrl refers to a sub-page, that is the
- // real page we are interested in.
- if ( absUrl.indexOf( "&" + $.mobile.subPageUrlKey ) > -1 ) {
- page = settings.pageContainer.children( ":jqmData(url='" + dataUrl + "')" );
- }
-
- //bind pageHide to removePage after it's hidden, if the page options specify to do so
-
- // Remove loading message.
- if ( settings.showLoadMsg ) {
- hideMsg();
- }
-
- deferred.resolve( absUrl, options, page, dupCachedPage );
- },
- error: function() {
- //set base back to current path
- if( base ) {
- base.set( path.get() );
- }
-
- // Remove loading message.
- if ( settings.showLoadMsg ) {
-
- // Remove loading message.
- hideMsg();
-
- //show error message
- $( "<div class='ui-loader ui-overlay-shadow ui-body-e ui-corner-all'><h1>"+ $.mobile.pageLoadErrorMessage +"</h1></div>" )
- .css({ "display": "block", "opacity": 0.96, "top": $window.scrollTop() + 100 })
- .appendTo( settings.pageContainer )
- .delay( 800 )
- .fadeOut( 400, function() {
- $( this ).remove();
- });
- }
-
- deferred.reject( absUrl, options );
- }
- });
- }
-
- return deferred.promise();
- };
-
- $.mobile.loadPage.defaults = {
- type: "get",
- data: undefined,
- reloadPage: false,
- role: undefined, // By default we rely on the role defined by the @data-role attribute.
- showLoadMsg: false,
- pageContainer: undefined,
- loadMsgDelay: 50 // This delay allows loads that pull from browser cache to occur without showing the loading message.
- };
-
- // Show a specific page in the page container.
- $.mobile.changePage = function( toPage, options ) {
- // XXX: REMOVE_BEFORE_SHIPPING_1.0
- // This is temporary code that makes changePage() compatible with previous alpha versions.
- if ( typeof options !== "object" ) {
- var opts = null;
-
- // Map old-style call signature for form submit to the new options object format.
- if ( typeof toPage === "object" && toPage.url && toPage.type ) {
- opts = {
- type: toPage.type,
- data: toPage.data,
- forcePageLoad: true
- };
- toPage = toPage.url;
- }
-
- // The arguments passed into the function need to be re-mapped
- // to the new options object format.
- var len = arguments.length;
- if ( len > 1 ) {
- var argNames = [ "transition", "reverse", "changeHash", "fromHashChange" ], i;
- for ( i = 1; i < len; i++ ) {
- var a = arguments[ i ];
- if ( typeof a !== "undefined" ) {
- opts = opts || {};
- opts[ argNames[ i - 1 ] ] = a;
- }
- }
- }
-
- // If an options object was created, then we know changePage() was called
- // with an old signature.
- if ( opts ) {
- return $.mobile.changePage( toPage, opts );
- }
- }
- // XXX: REMOVE_BEFORE_SHIPPING_1.0
-
- // If we are in the midst of a transition, queue the current request.
- // We'll call changePage() once we're done with the current transition to
- // service the request.
- if( isPageTransitioning ) {
- pageTransitionQueue.unshift( arguments );
- return;
- }
-
- var settings = $.extend( {}, $.mobile.changePage.defaults, options );
-
- // Make sure we have a pageContainer to work with.
- settings.pageContainer = settings.pageContainer || $.mobile.pageContainer;
-
- // Make sure we have a fromPage.
- settings.fromPage = settings.fromPage || $.mobile.activePage;
-
- var mpc = settings.pageContainer,
- pbcEvent = new $.Event( "pagebeforechange" ),
- triggerData = { toPage: toPage, options: settings };
-
- // Let listeners know we're about to change the current page.
- mpc.trigger( pbcEvent, triggerData );
-
- mpc.trigger( "beforechangepage", triggerData ); // XXX: DEPRECATED for 1.0
-
- // If the default behavior is prevented, stop here!
- if( pbcEvent.isDefaultPrevented() ){
- return;
- }
-
- // We allow "pagebeforechange" observers to modify the toPage in the trigger
- // data to allow for redirects. Make sure our toPage is updated.
-
- toPage = triggerData.toPage;
-
- // Set the isPageTransitioning flag to prevent any requests from
- // entering this method while we are in the midst of loading a page
- // or transitioning.
-
- isPageTransitioning = true;
-
- // If the caller passed us a url, call loadPage()
- // to make sure it is loaded into the DOM. We'll listen
- // to the promise object it returns so we know when
- // it is done loading or if an error ocurred.
- if ( typeof toPage == "string" ) {
- $.mobile.loadPage( toPage, settings )
- .done(function( url, options, newPage, dupCachedPage ) {
- isPageTransitioning = false;
- options.duplicateCachedPage = dupCachedPage;
- $.mobile.changePage( newPage, options );
- })
- .fail(function( url, options ) {
- // XXX_jblas: Fire off changepagefailed notificaiton.
- isPageTransitioning = false;
-
- //clear out the active button state
- removeActiveLinkClass( true );
-
- //release transition lock so navigation is free again
- releasePageTransitionLock();
- settings.pageContainer.trigger( "pagechangefailed", triggerData );
- settings.pageContainer.trigger( "changepagefailed", triggerData ); // XXX: DEPRECATED for 1.0
- });
- return;
- }
-
- // The caller passed us a real page DOM element. Update our
- // internal state and then trigger a transition to the page.
- var fromPage = settings.fromPage,
- url = ( settings.dataUrl && path.convertUrlToDataUrl( settings.dataUrl ) ) || toPage.jqmData( "url" ),
- // The pageUrl var is usually the same as url, except when url is obscured as a dialog url. pageUrl always contains the file path
- pageUrl = url,
- fileUrl = path.getFilePath( url ),
- active = urlHistory.getActive(),
- activeIsInitialPage = urlHistory.activeIndex === 0,
- historyDir = 0,
- pageTitle = document.title,
- isDialog = settings.role === "dialog" || toPage.jqmData( "role" ) === "dialog";
-
- // If we are trying to transition to the same page that we are currently on ignore the request.
- // an illegal same page request is defined by the current page being the same as the url, as long as there's history
- // and toPage is not an array or object (those are allowed to be "same")
- //
- // XXX_jblas: We need to remove this at some point when we allow for transitions
- // to the same page.
- if( fromPage && fromPage[0] === toPage[0] ) {
- isPageTransitioning = false;
- mpc.trigger( "pagechange", triggerData );
- mpc.trigger( "changepage", triggerData ); // XXX: DEPRECATED for 1.0
- return;
- }
-
- // We need to make sure the page we are given has already been enhanced.
- enhancePage( toPage, settings.role );
-
- // If the changePage request was sent from a hashChange event, check to see if the
- // page is already within the urlHistory stack. If so, we'll assume the user hit
- // the forward/back button and will try to match the transition accordingly.
- if( settings.fromHashChange ) {
- urlHistory.directHashChange({
- currentUrl: url,
- isBack: function() { historyDir = -1; },
- isForward: function() { historyDir = 1; }
- });
- }
-
- // Kill the keyboard.
- // XXX_jblas: We need to stop crawling the entire document to kill focus. Instead,
- // we should be tracking focus with a live() handler so we already have
- // the element in hand at this point.
- // Wrap this in a try/catch block since IE9 throw "Unspecified error" if document.activeElement
- // is undefined when we are in an IFrame.
- try {
- $( document.activeElement || "" ).add( "input:focus, textarea:focus, select:focus" ).blur();
- } catch(e) {}
-
- // If we're displaying the page as a dialog, we don't want the url
- // for the dialog content to be used in the hash. Instead, we want
- // to append the dialogHashKey to the url of the current page.
- if ( isDialog && active ) {
- // on the initial page load active.url is undefined and in that case should
- // be an empty string. Moving the undefined -> empty string back into
- // urlHistory.addNew seemed imprudent given undefined better represents
- // the url state
- url = ( active.url || "" ) + dialogHashKey;
- }
-
- // Set the location hash.
- if( settings.changeHash !== false && url ) {
- //disable hash listening temporarily
- urlHistory.ignoreNextHashChange = true;
- //update hash and history
- path.set( url );
- }
-
- //if title element wasn't found, try the page div data attr too
- var newPageTitle = toPage.jqmData( "title" ) || toPage.children(":jqmData(role='header')").find(".ui-title" ).text();
- if( !!newPageTitle && pageTitle == document.title ) {
- pageTitle = newPageTitle;
- }
-
- //add page to history stack if it's not back or forward
- if( !historyDir ) {
- urlHistory.addNew( url, settings.transition, pageTitle, pageUrl, settings.role );
- }
-
- //set page title
- document.title = urlHistory.getActive().title;
-
- //set "toPage" as activePage
- $.mobile.activePage = toPage;
-
- // Make sure we have a transition defined.
- settings.transition = settings.transition
- || ( ( historyDir && !activeIsInitialPage ) ? active.transition : undefined )
- || ( isDialog ? $.mobile.defaultDialogTransition : $.mobile.defaultPageTransition );
-
- // If we're navigating back in the URL history, set reverse accordingly.
- settings.reverse = settings.reverse || historyDir < 0;
-
- transitionPages( toPage, fromPage, settings.transition, settings.reverse )
- .done(function() {
- removeActiveLinkClass();
-
- //if there's a duplicateCachedPage, remove it from the DOM now that it's hidden
- if ( settings.duplicateCachedPage ) {
- settings.duplicateCachedPage.remove();
- }
-
- //remove initial build class (only present on first pageshow)
- $html.removeClass( "ui-mobile-rendering" );
-
- releasePageTransitionLock();
-
- // Let listeners know we're all done changing the current page.
- mpc.trigger( "pagechange", triggerData );
-
- mpc.trigger( "changepage", triggerData ); // XXX: DEPRECATED for 1.0
- });
- };
-
- $.mobile.changePage.defaults = {
- transition: undefined,
- reverse: false,
- changeHash: true,
- fromHashChange: false,
- role: undefined, // By default we rely on the role defined by the @data-role attribute.
- duplicateCachedPage: undefined,
- pageContainer: undefined,
- showLoadMsg: true, //loading message shows by default when pages are being fetched during changePage
- dataUrl: undefined,
- fromPage: undefined
- };
-
-/* Event Bindings - hashchange, submit, and click */
- function findClosestLink( ele )
- {
- while ( ele ) {
- if ( ele.nodeName.toLowerCase() == "a" ) {
- break;
- }
- ele = ele.parentNode;
- }
- return ele;
- }
-
- // The base URL for any given element depends on the page it resides in.
- function getClosestBaseUrl( ele )
- {
- // Find the closest page and extract out its url.
- var url = $( ele ).closest( ".ui-page" ).jqmData( "url" ),
- base = documentBase.hrefNoHash;
-
- if ( !url || !path.isPath( url ) ) {
- url = base;
- }
-
- return path.makeUrlAbsolute( url, base);
- }
-
-
- //The following event bindings should be bound after mobileinit has been triggered
- //the following function is called in the init file
- $.mobile._registerInternalEvents = function(){
-
- //bind to form submit events, handle with Ajax
- $( "form" ).live('submit', function( event ) {
- var $this = $( this );
- if( !$.mobile.ajaxEnabled ||
- $this.is( ":jqmData(ajax='false')" ) ) {
- return;
- }
-
- var type = $this.attr( "method" ),
- target = $this.attr( "target" ),
- url = $this.attr( "action" );
-
- // If no action is specified, browsers default to using the
- // URL of the document containing the form. Since we dynamically
- // pull in pages from external documents, the form should submit
- // to the URL for the source document of the page containing
- // the form.
- if ( !url ) {
- // Get the @data-url for the page containing the form.
- url = getClosestBaseUrl( $this );
- if ( url === documentBase.hrefNoHash ) {
- // The url we got back matches the document base,
- // which means the page must be an internal/embedded page,
- // so default to using the actual document url as a browser
- // would.
- url = documentUrl.hrefNoSearch;
- }
- }
-
- url = path.makeUrlAbsolute( url, getClosestBaseUrl($this) );
-
- //external submits use regular HTTP
- if( path.isExternal( url ) || target ) {
- return;
- }
-
- $.mobile.changePage(
- url,
- {
- type: type && type.length && type.toLowerCase() || "get",
- data: $this.serialize(),
- transition: $this.jqmData( "transition" ),
- direction: $this.jqmData( "direction" ),
- reloadPage: true
- }
- );
- event.preventDefault();
- });
-
- //add active state on vclick
- $( document ).bind( "vclick", function( event ) {
- var link = findClosestLink( event.target );
- if ( link ) {
- if ( path.parseUrl( link.getAttribute( "href" ) || "#" ).hash !== "#" ) {
- removeActiveLinkClass( true );
- $activeClickedLink = $( link ).closest( ".ui-btn" ).not( ".ui-disabled" );
- $activeClickedLink.addClass( $.mobile.activeBtnClass );
- $( "." + $.mobile.activePageClass + " .ui-btn" ).not( link ).blur();
- }
- }
- });
-
- // click routing - direct to HTTP or Ajax, accordingly
- $( document ).bind( "click", function( event ) {
- var link = findClosestLink( event.target );
- if ( !link ) {
- return;
- }
-
- var $link = $( link ),
- //remove active link class if external (then it won't be there if you come back)
- httpCleanup = function(){
- window.setTimeout( function() { removeActiveLinkClass( true ); }, 200 );
- };
-
- //if there's a data-rel=back attr, go back in history
- if( $link.is( ":jqmData(rel='back')" ) ) {
- window.history.back();
- return false;
- }
-
- //if ajax is disabled, exit early
- if( !$.mobile.ajaxEnabled ){
- httpCleanup();
- //use default click handling
- return;
- }
-
- var baseUrl = getClosestBaseUrl( $link ),
-
- //get href, if defined, otherwise default to empty hash
- href = path.makeUrlAbsolute( $link.attr( "href" ) || "#", baseUrl );
-
- // XXX_jblas: Ideally links to application pages should be specified as
- // an url to the application document with a hash that is either
- // the site relative path or id to the page. But some of the
- // internal code that dynamically generates sub-pages for nested
- // lists and select dialogs, just write a hash in the link they
- // create. This means the actual URL path is based on whatever
- // the current value of the base tag is at the time this code
- // is called. For now we are just assuming that any url with a
- // hash in it is an application page reference.
- if ( href.search( "#" ) != -1 ) {
- href = href.replace( /[^#]*#/, "" );
- if ( !href ) {
- //link was an empty hash meant purely
- //for interaction, so we ignore it.
- event.preventDefault();
- return;
- } else if ( path.isPath( href ) ) {
- //we have apath so make it the href we want to load.
- href = path.makeUrlAbsolute( href, baseUrl );
- } else {
- //we have a simple id so use the documentUrl as its base.
- href = path.makeUrlAbsolute( "#" + href, documentUrl.hrefNoHash );
- }
- }
-
- // Should we handle this link, or let the browser deal with it?
- var useDefaultUrlHandling = $link.is( "[rel='external']" ) || $link.is( ":jqmData(ajax='false')" ) || $link.is( "[target]" ),
-
- // Some embedded browsers, like the web view in Phone Gap, allow cross-domain XHR
- // requests if the document doing the request was loaded via the file:// protocol.
- // This is usually to allow the application to "phone home" and fetch app specific
- // data. We normally let the browser handle external/cross-domain urls, but if the
- // allowCrossDomainPages option is true, we will allow cross-domain http/https
- // requests to go through our page loading logic.
- isCrossDomainPageLoad = ( $.mobile.allowCrossDomainPages && documentUrl.protocol === "file:" && href.search( /^https?:/ ) != -1 ),
-
- //check for protocol or rel and its not an embedded page
- //TODO overlap in logic from isExternal, rel=external check should be
- // moved into more comprehensive isExternalLink
- isExternal = useDefaultUrlHandling || ( path.isExternal( href ) && !isCrossDomainPageLoad );
-
- if( isExternal ) {
- httpCleanup();
- //use default click handling
- return;
- }
-
- //use ajax
- var transition = $link.jqmData( "transition" ),
- direction = $link.jqmData( "direction" ),
- reverse = ( direction && direction === "reverse" ) ||
- // deprecated - remove by 1.0
- $link.jqmData( "back" ),
-
- //this may need to be more specific as we use data-rel more
- role = $link.attr( "data-" + $.mobile.ns + "rel" ) || undefined;
-
- $.mobile.changePage( href, { transition: transition, reverse: reverse, role: role } );
- event.preventDefault();
- });
-
- //prefetch pages when anchors with data-prefetch are encountered
- $( ".ui-page" ).live( "pageshow.prefetch", function(){
- var urls = [];
- $( this ).find( "a:jqmData(prefetch)" ).each(function(){
- var url = $( this ).attr( "href" );
- if ( url && $.inArray( url, urls ) === -1 ) {
- urls.push( url );
- $.mobile.loadPage( url );
- }
- });
- } );
-
- $.mobile._handleHashChange = function( hash ) {
- //find first page via hash
- var to = path.stripHash( hash ),
- //transition is false if it's the first page, undefined otherwise (and may be overridden by default)
- transition = $.mobile.urlHistory.stack.length === 0 ? "none" : undefined,
-
- // default options for the changPage calls made after examining the current state
- // of the page and the hash
- changePageOptions = {
- transition: transition,
- changeHash: false,
- fromHashChange: true
- };
-
- //if listening is disabled (either globally or temporarily), or it's a dialog hash
- if( !$.mobile.hashListeningEnabled || urlHistory.ignoreNextHashChange ) {
- urlHistory.ignoreNextHashChange = false;
- return;
- }
-
- // special case for dialogs
- if( urlHistory.stack.length > 1 && to.indexOf( dialogHashKey ) > -1 ) {
-
- // If current active page is not a dialog skip the dialog and continue
- // in the same direction
- if(!$.mobile.activePage.is( ".ui-dialog" )) {
- //determine if we're heading forward or backward and continue accordingly past
- //the current dialog
- urlHistory.directHashChange({
- currentUrl: to,
- isBack: function() { window.history.back(); },
- isForward: function() { window.history.forward(); }
- });
-
- // prevent changepage
- return;
- } else {
- // if the current active page is a dialog and we're navigating
- // to a dialog use the dialog objected saved in the stack
- urlHistory.directHashChange({
- currentUrl: to,
-
- // regardless of the direction of the history change
- // do the following
- either: function( isBack ) {
- var active = $.mobile.urlHistory.getActive();
-
- to = active.pageUrl;
-
- // make sure to set the role, transition and reversal
- // as most of this is lost by the domCache cleaning
- $.extend( changePageOptions, {
- role: active.role,
- transition: active.transition,
- reverse: isBack
- });
- }
- });
- }
- }
-
- //if to is defined, load it
- if ( to ) {
- to = ( typeof to === "string" && !path.isPath( to ) ) ? ( '#' + to ) : to;
- $.mobile.changePage( to, changePageOptions );
- } else {
- //there's no hash, go to the first page in the dom
- $.mobile.changePage( $.mobile.firstPage, changePageOptions );
- }
- };
-
- //hashchange event handler
- $window.bind( "hashchange", function( e, triggered ) {
- $.mobile._handleHashChange( location.hash );
- });
-
- //set page min-heights to be device specific
- $( document ).bind( "pageshow", resetActivePageHeight );
- $( window ).bind( "throttledresize", resetActivePageHeight );
-
- };//_registerInternalEvents callback
-
-})( jQuery );
-/*
-* jQuery Mobile Framework : history.pushState support, layered on top of hashchange
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-( function( $, window ) {
- // For now, let's Monkeypatch this onto the end of $.mobile._registerInternalEvents
- // Scope self to pushStateHandler so we can reference it sanely within the
- // methods handed off as event handlers
- var pushStateHandler = {},
- self = pushStateHandler,
- $win = $( window ),
- url = $.mobile.path.parseUrl( location.href );
-
- $.extend( pushStateHandler, {
- // TODO move to a path helper, this is rather common functionality
- initialFilePath: (function() {
- return url.pathname + url.search;
- })(),
-
- initialHref: url.hrefNoHash,
-
- // Flag for tracking if a Hashchange naturally occurs after each popstate + replace
- hashchangeFired: false,
-
- state: function() {
- return {
- hash: location.hash || "#" + self.initialFilePath,
- title: document.title,
-
- // persist across refresh
- initialHref: self.initialHref
- };
- },
-
- resetUIKeys: function( url ) {
- var dialog = $.mobile.dialogHashKey,
- subkey = "&" + $.mobile.subPageUrlKey,
- dialogIndex = url.indexOf( dialog );
-
- if( dialogIndex > -1 ) {
- url = url.slice( 0, dialogIndex ) + "#" + url.slice( dialogIndex );
- } else if( url.indexOf( subkey ) > -1 ) {
- url = url.split( subkey ).join( "#" + subkey );
- }
-
- return url;
- },
-
- // on hash change we want to clean up the url
- // NOTE this takes place *after* the vanilla navigation hash change
- // handling has taken place and set the state of the DOM
- onHashChange: function( e ) {
- var href, state;
-
- self.hashchangeFired = true;
-
- // only replaceState when the hash doesn't represent an embeded page
- if( $.mobile.path.isPath(location.hash) ) {
-
- // propulate the hash when its not available
- state = self.state();
-
- // make the hash abolute with the current href
- href = $.mobile.path.makeUrlAbsolute( state.hash.replace("#", ""), location.href );
-
- href = self.resetUIKeys( href );
-
- // replace the current url with the new href and store the state
- history.replaceState( state, document.title, href );
- }
- },
-
- // on popstate (ie back or forward) we need to replace the hash that was there previously
- // cleaned up by the additional hash handling
- onPopState: function( e ) {
- var poppedState = e.originalEvent.state, holdnexthashchange = false;
-
- // if there's no state its not a popstate we care about, ie chrome's initial popstate
- // or forward popstate
- if( poppedState ) {
- // disable any hashchange triggered by the browser
- $.mobile.urlHistory.ignoreNextHashChange = true;
-
- // defer our manual hashchange until after the browser fired
- // version has come and gone
- setTimeout(function() {
- // make sure that the manual hash handling takes place
- $.mobile.urlHistory.ignoreNextHashChange = false;
-
- // change the page based on the hash
- $.mobile._handleHashChange( poppedState.hash );
- }, 100);
- }
- },
-
- init: function() {
- $win.bind( "hashchange", self.onHashChange );
-
- // Handle popstate events the occur through history changes
- $win.bind( "popstate", self.onPopState );
-
- // if there's no hash, we need to replacestate for returning to home
- if ( location.hash === "" ) {
- history.replaceState( self.state(), document.title, location.href );
- }
- }
- });
-
- $( function() {
- if( $.mobile.pushStateEnabled && $.support.pushState ){
- pushStateHandler.init();
- }
- });
-})( jQuery, this );/*!
- * jQuery Mobile v@VERSION
- * http://jquerymobile.com/
- *
- * Copyright 2010, jQuery Project
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- */
-
-(function( $, window, undefined ) {
-
-function css3TransitionHandler( name, reverse, $to, $from ) {
-
- var deferred = new $.Deferred(),
- reverseClass = reverse ? " reverse" : "",
- viewportClass = "ui-mobile-viewport-transitioning viewport-" + name,
- doneFunc = function() {
-
- $to.add( $from ).removeClass( "out in reverse " + name );
-
- if ( $from ) {
- $from.removeClass( $.mobile.activePageClass );
- }
-
- $to.parent().removeClass( viewportClass );
-
- deferred.resolve( name, reverse, $to, $from );
- };
-
- $to.animationComplete( doneFunc );
-
- $to.parent().addClass( viewportClass );
-
- if ( $from ) {
- $from.addClass( name + " out" + reverseClass );
- }
- $to.addClass( $.mobile.activePageClass + " " + name + " in" + reverseClass );
-
- return deferred.promise();
-}
-
-// Make our transition handler public.
-$.mobile.css3TransitionHandler = css3TransitionHandler;
-
-// If the default transition handler is the 'none' handler, replace it with our handler.
-if ( $.mobile.defaultTransitionHandler === $.mobile.noneTransitionHandler ) {
- $.mobile.defaultTransitionHandler = css3TransitionHandler;
-}
-
-})( jQuery, this );
-/*
-* jQuery Mobile Framework : "degradeInputs" plugin - degrades inputs to another type after custom enhancements are made.
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-
-(function( $, undefined ) {
-
-$.mobile.page.prototype.options.degradeInputs = {
- color: false,
- date: false,
- datetime: false,
- "datetime-local": false,
- email: false,
- month: false,
- number: false,
- range: "number",
- search: "text",
- tel: false,
- time: false,
- url: false,
- week: false
-};
-
-$.mobile.page.prototype.options.keepNative = ":jqmData(role='none'), :jqmData(role='nojs')";
-
-
-//auto self-init widgets
-$( document ).bind( "pagecreate enhance", function( e ){
-
- var page = $( e.target ).data( "page" ),
- o = page.options;
-
- // degrade inputs to avoid poorly implemented native functionality
- $( e.target ).find( "input" ).not( o.keepNative ).each(function() {
- var $this = $( this ),
- type = this.getAttribute( "type" ),
- optType = o.degradeInputs[ type ] || "text";
-
- if ( o.degradeInputs[ type ] ) {
- $this.replaceWith(
- $( "<div>" ).html( $this.clone() ).html()
- .replace( /\s+type=["']?\w+['"]?/, " type=\"" + optType + "\" data-" + $.mobile.ns + "type=\"" + type + "\" " )
- );
- }
- });
-
-});
-
-})( jQuery );/*
-* jQuery Mobile Framework : "dialog" plugin.
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
-*/
-
-(function( $, window, undefined ) {
-
-$.widget( "mobile.dialog", $.mobile.widget, {
- options: {
- closeBtnText : "Close",
- theme : "a",
- initSelector : ":jqmData(role='dialog')"
- },
- _create: function() {
- var $el = this.element,
- pageTheme = $el.attr( "class" ).match( /ui-body-[a-z]/ );
-
- if( pageTheme.length ){
- $el.removeClass( pageTheme[ 0 ] );
- }
-
- $el.addClass( "ui-body-" + this.options.theme );
-
- // Class the markup for dialog styling
- // Set aria role
- $el.attr( "role", "dialog" )
- .addClass( "ui-dialog" )
- .find( ":jqmData(role='header')" )
- .addClass( "ui-corner-top ui-overlay-shadow" )
- .prepend( "<a href='#' data-" + $.mobile.ns + "icon='delete' data-" + $.mobile.ns + "rel='back' data-" + $.mobile.ns + "iconpos='notext'>"+ this.options.closeBtnText + "</a>" )
- .end()
- .find( ":jqmData(role='content'),:jqmData(role='footer')" )
- .last()
- .addClass( "ui-corner-bottom ui-overlay-shadow" );
-
- /* bind events
- - clicks and submits should use the closing transition that the dialog opened with
- unless a data-transition is specified on the link/form
- - if the click was on the close button, or the link has a data-rel="back" it'll go back in history naturally
- */
- $el.bind( "vclick submit", function( event ) {
- var $target = $( event.target ).closest( event.type === "vclick" ? "a" : "form" ),
- active;
-
- if ( $target.length && !$target.jqmData( "transition" ) ) {
-
- active = $.mobile.urlHistory.getActive() || {};
-
- $target.attr( "data-" + $.mobile.ns + "transition", ( active.transition || $.mobile.defaultDialogTransition ) )
- .attr( "data-" + $.mobile.ns + "direction", "reverse" );
- }
- })
- .bind( "pagehide", function() {
- $( this ).find( "." + $.mobile.activeBtnClass ).removeClass( $.mobile.activeBtnClass );
- });
- },
-
- // Close method goes back in history
- close: function() {
- window.history.back();
- }
-});
-
-//auto self-init widgets
-$( $.mobile.dialog.prototype.options.initSelector ).live( "pagecreate", function(){
- $( this ).dialog();
-});
-
-})( jQuery, this );
-/*
-* jQuery Mobile Framework : This plugin handles theming and layout of headers, footers, and content areas
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-
-(function( $, undefined ) {
-
-$.mobile.page.prototype.options.backBtnText = "Back";
-$.mobile.page.prototype.options.addBackBtn = false;
-$.mobile.page.prototype.options.backBtnTheme = null;
-$.mobile.page.prototype.options.headerTheme = "a";
-$.mobile.page.prototype.options.footerTheme = "a";
-$.mobile.page.prototype.options.contentTheme = null;
-
-$( ":jqmData(role='page'), :jqmData(role='dialog')" ).live( "pagecreate", function( e ) {
-
- var $page = $( this ),
- o = $page.data( "page" ).options,
- pageTheme = o.theme;
-
- $( ":jqmData(role='header'), :jqmData(role='footer'), :jqmData(role='content')", this ).each(function() {
- var $this = $( this ),
- role = $this.jqmData( "role" ),
- theme = $this.jqmData( "theme" ),
- $headeranchors,
- leftbtn,
- rightbtn,
- backBtn;
-
- $this.addClass( "ui-" + role );
-
- //apply theming and markup modifications to page,header,content,footer
- if ( role === "header" || role === "footer" ) {
-
- var thisTheme = theme || ( role === "header" ? o.headerTheme : o.footerTheme ) || pageTheme;
-
- //add theme class
- $this.addClass( "ui-bar-" + thisTheme );
-
- // Add ARIA role
- $this.attr( "role", role === "header" ? "banner" : "contentinfo" );
-
- // Right,left buttons
- $headeranchors = $this.children( "a" );
- leftbtn = $headeranchors.hasClass( "ui-btn-left" );
- rightbtn = $headeranchors.hasClass( "ui-btn-right" );
-
- if ( !leftbtn ) {
- leftbtn = $headeranchors.eq( 0 ).not( ".ui-btn-right" ).addClass( "ui-btn-left" ).length;
- }
-
- if ( !rightbtn ) {
- rightbtn = $headeranchors.eq( 1 ).addClass( "ui-btn-right" ).length;
- }
-
- // Auto-add back btn on pages beyond first view
- if ( o.addBackBtn && role === "header" &&
- $( ".ui-page" ).length > 1 &&
- $this.jqmData( "url" ) !== $.mobile.path.stripHash( location.hash ) &&
- !leftbtn ) {
-
- backBtn = $( "<a href='#' class='ui-btn-left' data-"+ $.mobile.ns +"rel='back' data-"+ $.mobile.ns +"icon='arrow-l'>"+ o.backBtnText +"</a>" ).prependTo( $this );
-
- // If theme is provided, override default inheritance
- backBtn.attr( "data-"+ $.mobile.ns +"theme", o.backBtnTheme || thisTheme );
- }
-
- // Page title
- $this.children( "h1, h2, h3, h4, h5, h6" )
- .addClass( "ui-title" )
- // Regardless of h element number in src, it becomes h1 for the enhanced page
- .attr({
- "tabindex": "0",
- "role": "heading",
- "aria-level": "1"
- });
-
- } else if ( role === "content" ) {
-
- if (theme || o.contentTheme) {
- $this.addClass( "ui-body-" + ( theme || o.contentTheme ) );
- }
-
- // Add ARIA role
- $this.attr( "role", "main" );
-
- }
- });
-});
-
-})( jQuery );/*
-* jQuery Mobile Framework : "collapsible" plugin
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-(function( $, undefined ) {
-
-$.widget( "mobile.collapsible", $.mobile.widget, {
- options: {
- expandCueText: " click to expand contents",
- collapseCueText: " click to collapse contents",
- collapsed: true,
- heading: ">:header,>legend",
- theme: null,
- iconTheme: "d",
- initSelector: ":jqmData(role='collapsible')"
- },
- _create: function() {
-
- var $el = this.element,
- o = this.options,
- collapsibleContain = $el.addClass( "ui-collapsible-contain" ),
- collapsibleHeading = $el.find( o.heading ).eq( 0 ),
- collapsibleContent = collapsibleContain.wrapInner( "<div class='ui-collapsible-content'></div>" ).find( ".ui-collapsible-content" ),
- collapsibleParent = $el.closest( ":jqmData(role='collapsible-set')" ).addClass( "ui-collapsible-set" );
-
- // Replace collapsibleHeading if it's a legend
- if ( collapsibleHeading.is( "legend" ) ) {
- collapsibleHeading = $( "<div role='heading'>"+ collapsibleHeading.html() +"</div>" ).insertBefore( collapsibleHeading );
- collapsibleHeading.next().remove();
- }
-
- collapsibleHeading
- //drop heading in before content
- .insertBefore( collapsibleContent )
- //modify markup & attributes
- .addClass( "ui-collapsible-heading" )
- .append( "<span class='ui-collapsible-heading-status'></span>" )
- .wrapInner( "<a href='#' class='ui-collapsible-heading-toggle'></a>" )
- .find( "a:eq(0)" )
- .buttonMarkup({
- shadow: !collapsibleParent.length,
- corners: false,
- iconPos: "left",
- icon: "plus",
- theme: o.theme
- })
- .find( ".ui-icon" )
- .removeAttr( "class" )
- .buttonMarkup({
- shadow: true,
- corners: true,
- iconPos: "notext",
- icon: "plus",
- theme: o.iconTheme
- });
-
- if ( !collapsibleParent.length ) {
- collapsibleHeading
- .find( "a:eq(0)" )
- .addClass( "ui-corner-all" )
- .find( ".ui-btn-inner" )
- .addClass( "ui-corner-all" );
- } else {
- if ( collapsibleContain.jqmData( "collapsible-last" ) ) {
- collapsibleHeading
- .find( "a:eq(0), .ui-btn-inner" )
- .addClass( "ui-corner-bottom" );
- }
- }
-
- //events
- collapsibleContain
- .bind( "collapse", function( event ) {
- if ( ! event.isDefaultPrevented() &&
- $( event.target ).closest( ".ui-collapsible-contain" ).is( collapsibleContain ) ) {
-
- event.preventDefault();
-
- collapsibleHeading
- .addClass( "ui-collapsible-heading-collapsed" )
- .find( ".ui-collapsible-heading-status" )
- .text( o.expandCueText )
- .end()
- .find( ".ui-icon" )
- .removeClass( "ui-icon-minus" )
- .addClass( "ui-icon-plus" );
-
- collapsibleContent.addClass( "ui-collapsible-content-collapsed" ).attr( "aria-hidden", true );
-
- if ( collapsibleContain.jqmData( "collapsible-last" ) ) {
- collapsibleHeading
- .find( "a:eq(0), .ui-btn-inner" )
- .addClass( "ui-corner-bottom" );
- }
- }
- })
- .bind( "expand", function( event ) {
- if ( !event.isDefaultPrevented() ) {
-
- event.preventDefault();
-
- collapsibleHeading
- .removeClass( "ui-collapsible-heading-collapsed" )
- .find( ".ui-collapsible-heading-status" ).text( o.collapseCueText );
-
- collapsibleHeading.find( ".ui-icon" ).removeClass( "ui-icon-plus" ).addClass( "ui-icon-minus" );
-
- collapsibleContent.removeClass( "ui-collapsible-content-collapsed" ).attr( "aria-hidden", false );
-
- if ( collapsibleContain.jqmData( "collapsible-last" ) ) {
-
- collapsibleHeading
- .find( "a:eq(0), .ui-btn-inner" )
- .removeClass( "ui-corner-bottom" );
- }
- }
- })
- .trigger( o.collapsed ? "collapse" : "expand" );
-
- // Close others in a set
- if ( collapsibleParent.length && !collapsibleParent.jqmData( "collapsiblebound" ) ) {
-
- collapsibleParent
- .jqmData( "collapsiblebound", true )
- .bind( "expand", function( event ) {
-
- $( event.target )
- .closest( ".ui-collapsible-contain" )
- .siblings( ".ui-collapsible-contain" )
- .trigger( "collapse" );
-
- });
-
- var set = collapsibleParent.children( ":jqmData(role='collapsible')" );
-
- set.first()
- .find( "a:eq(0)" )
- .addClass( "ui-corner-top" )
- .find( ".ui-btn-inner" )
- .addClass( "ui-corner-top" );
-
- set.last().jqmData( "collapsible-last", true );
- }
-
- collapsibleHeading
- .bind( "vclick", function( event ) {
-
- var type = collapsibleHeading.is( ".ui-collapsible-heading-collapsed" ) ?
- "expand" : "collapse";
-
- collapsibleContain.trigger( type );
-
- event.preventDefault();
- });
- }
-});
-
-//auto self-init widgets
-$( document ).bind( "pagecreate create", function( e ){
- $( $.mobile.collapsible.prototype.options.initSelector, e.target ).collapsible();
-});
-
-})( jQuery );
-/*
-* jQuery Mobile Framework : "fieldcontain" plugin - simple class additions to make form row separators
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-
-(function( $, undefined ) {
-
-$.fn.fieldcontain = function( options ) {
- return this.addClass( "ui-field-contain ui-body ui-br" );
-};
-
-//auto self-init widgets
-$( document ).bind( "pagecreate create", function( e ){
- $( ":jqmData(role='fieldcontain')", e.target ).fieldcontain();
-});
-
-})( jQuery );/*
-* jQuery Mobile Framework : plugin for creating CSS grids
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-
-(function( $, undefined ) {
-
-$.fn.grid = function( options ) {
- return this.each(function() {
-
- var $this = $( this ),
- o = $.extend({
- grid: null
- },options),
- $kids = $this.children(),
- gridCols = {solo:1, a:2, b:3, c:4, d:5},
- grid = o.grid,
- iterator;
-
- if ( !grid ) {
- if ( $kids.length <= 5 ) {
- for ( var letter in gridCols ) {
- if ( gridCols[ letter ] === $kids.length ) {
- grid = letter;
- }
- }
- } else {
- grid = "a";
- }
- }
- iterator = gridCols[grid];
-
- $this.addClass( "ui-grid-" + grid );
-
- $kids.filter( ":nth-child(" + iterator + "n+1)" ).addClass( "ui-block-a" );
-
- if ( iterator > 1 ) {
- $kids.filter( ":nth-child(" + iterator + "n+2)" ).addClass( "ui-block-b" );
- }
- if ( iterator > 2 ) {
- $kids.filter( ":nth-child(3n+3)" ).addClass( "ui-block-c" );
- }
- if ( iterator > 3 ) {
- $kids.filter( ":nth-child(4n+4)" ).addClass( "ui-block-d" );
- }
- if ( iterator > 4 ) {
- $kids.filter( ":nth-child(5n+5)" ).addClass( "ui-block-e" );
- }
- });
-};
-})( jQuery );/*
-* jQuery Mobile Framework : "navbar" plugin
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-
-(function( $, undefined ) {
-
-$.widget( "mobile.navbar", $.mobile.widget, {
- options: {
- iconpos: "top",
- grid: null,
- initSelector: ":jqmData(role='navbar')"
- },
-
- _create: function(){
-
- var $navbar = this.element,
- $navbtns = $navbar.find( "a" ),
- iconpos = $navbtns.filter( ":jqmData(icon)" ).length ?
- this.options.iconpos : undefined;
-
- $navbar.addClass( "ui-navbar" )
- .attr( "role","navigation" )
- .find( "ul" )
- .grid({ grid: this.options.grid });
-
- if ( !iconpos ) {
- $navbar.addClass( "ui-navbar-noicons" );
- }
-
- $navbtns.buttonMarkup({
- corners: false,
- shadow: false,
- iconpos: iconpos
- });
-
- $navbar.delegate( "a", "vclick", function( event ) {
- $navbtns.not( ".ui-state-persist" ).removeClass( $.mobile.activeBtnClass );
- $( this ).addClass( $.mobile.activeBtnClass );
- });
- }
-});
-
-//auto self-init widgets
-$( document ).bind( "pagecreate create", function( e ){
- $( $.mobile.navbar.prototype.options.initSelector, e.target ).navbar();
-});
-
-})( jQuery );
-/*
-* jQuery Mobile Framework : "listview" plugin
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-
-(function( $, undefined ) {
-
-//Keeps track of the number of lists per page UID
-//This allows support for multiple nested list in the same page
-//https://github.com/jquery/jquery-mobile/issues/1617
-var listCountPerPage = {};
-
-$.widget( "mobile.listview", $.mobile.widget, {
- options: {
- theme: "c",
- countTheme: "c",
- headerTheme: "b",
- dividerTheme: "b",
- splitIcon: "arrow-r",
- splitTheme: "b",
- inset: false,
- initSelector: ":jqmData(role='listview')"
- },
-
- _create: function() {
- var t = this;
-
- // create listview markup
- t.element.addClass(function( i, orig ) {
- return orig + " ui-listview " + ( t.options.inset ? " ui-listview-inset ui-corner-all ui-shadow " : "" );
- });
-
- t.refresh( true );
- },
-
- _itemApply: function( $list, item ) {
- var $countli = item.find( ".ui-li-count" );
- if ( $countli.length ) {
- item.addClass( "ui-li-has-count" );
- }
- $countli.addClass( "ui-btn-up-" + ( $list.jqmData( "counttheme" ) || this.options.countTheme ) + " ui-btn-corner-all" );
-
- // TODO class has to be defined in markup
- item.find( "h1, h2, h3, h4, h5, h6" ).addClass( "ui-li-heading" ).end()
- .find( "p, dl" ).addClass( "ui-li-desc" ).end()
- .find( ">img:eq(0), .ui-link-inherit>img:eq(0)" ).addClass( "ui-li-thumb" ).each(function() {
- item.addClass( $(this).is( ".ui-li-icon" ) ? "ui-li-has-icon" : "ui-li-has-thumb" );
- }).end()
- .find( ".ui-li-aside" ).each(function() {
- var $this = $(this);
- $this.prependTo( $this.parent() ); //shift aside to front for css float
- });
- },
-
- _removeCorners: function( li, which ) {
- var top = "ui-corner-top ui-corner-tr ui-corner-tl",
- bot = "ui-corner-bottom ui-corner-br ui-corner-bl";
-
- li = li.add( li.find( ".ui-btn-inner, .ui-li-link-alt, .ui-li-thumb" ) );
-
- if ( which === "top" ) {
- li.removeClass( top );
- } else if ( which === "bottom" ) {
- li.removeClass( bot );
- } else {
- li.removeClass( top + " " + bot );
- }
- },
-
- _refreshCorners: function( create ) {
- var $li,
- $visibleli,
- $topli,
- $bottomli;
-
- if ( this.options.inset ) {
- $li = this.element.children( "li" );
- // at create time the li are not visible yet so we need to rely on .ui-screen-hidden
- $visibleli = create?$li.not( ".ui-screen-hidden" ):$li.filter( ":visible" );
-
- this._removeCorners( $li );
-
- // Select the first visible li element
- $topli = $visibleli.first()
- .addClass( "ui-corner-top" );
-
- $topli.add( $topli.find( ".ui-btn-inner" ) )
- .find( ".ui-li-link-alt" )
- .addClass( "ui-corner-tr" )
- .end()
- .find( ".ui-li-thumb" )
- .addClass( "ui-corner-tl" );
-
- // Select the last visible li element
- $bottomli = $visibleli.last()
- .addClass( "ui-corner-bottom" );
-
- $bottomli.add( $bottomli.find( ".ui-btn-inner" ) )
- .find( ".ui-li-link-alt" )
- .addClass( "ui-corner-br" )
- .end()
- .find( ".ui-li-thumb" )
- .addClass( "ui-corner-bl" );
- }
- },
-
- refresh: function( create ) {
- this.parentPage = this.element.closest( ".ui-page" );
- this._createSubPages();
-
- var o = this.options,
- $list = this.element,
- self = this,
- dividertheme = $list.jqmData( "dividertheme" ) || o.dividerTheme,
- listsplittheme = $list.jqmData( "splittheme" ),
- listspliticon = $list.jqmData( "spliticon" ),
- li = $list.children( "li" ),
- counter = $.support.cssPseudoElement || !$.nodeName( $list[ 0 ], "ol" ) ? 0 : 1,
- item, itemClass, itemTheme,
- a, last, splittheme, countParent, icon;
-
- if ( counter ) {
- $list.find( ".ui-li-dec" ).remove();
- }
-
- for ( var pos = 0, numli = li.length; pos < numli; pos++ ) {
- item = li.eq( pos );
- itemClass = "ui-li";
-
- // If we're creating the element, we update it regardless
- if ( create || !item.hasClass( "ui-li" ) ) {
- itemTheme = item.jqmData("theme") || o.theme;
- a = item.children( "a" );
-
- if ( a.length ) {
- icon = item.jqmData("icon");
-
- item.buttonMarkup({
- wrapperEls: "div",
- shadow: false,
- corners: false,
- iconpos: "right",
- icon: a.length > 1 || icon === false ? false : icon || "arrow-r",
- theme: itemTheme
- });
-
- if ( ( icon != false ) && ( a.length == 1 ) ) {
- item.addClass( "ui-li-has-arrow" );
- }
-
- a.first().addClass( "ui-link-inherit" );
-
- if ( a.length > 1 ) {
- itemClass += " ui-li-has-alt";
-
- last = a.last();
- splittheme = listsplittheme || last.jqmData( "theme" ) || o.splitTheme;
-
- last.appendTo(item)
- .attr( "title", last.text() )
- .addClass( "ui-li-link-alt" )
- .empty()
- .buttonMarkup({
- shadow: false,
- corners: false,
- theme: itemTheme,
- icon: false,
- iconpos: false
- })
- .find( ".ui-btn-inner" )
- .append(
- $( "<span />" ).buttonMarkup({
- shadow: true,
- corners: true,
- theme: splittheme,
- iconpos: "notext",
- icon: listspliticon || last.jqmData( "icon" ) || o.splitIcon
- })
- );
- }
- } else if ( item.jqmData( "role" ) === "list-divider" ) {
-
- itemClass += " ui-li-divider ui-btn ui-bar-" + dividertheme;
- item.attr( "role", "heading" );
-
- //reset counter when a divider heading is encountered
- if ( counter ) {
- counter = 1;
- }
-
- } else {
- itemClass += " ui-li-static ui-body-" + itemTheme;
- }
- }
-
- if ( counter && itemClass.indexOf( "ui-li-divider" ) < 0 ) {
- countParent = item.is( ".ui-li-static:first" ) ? item : item.find( ".ui-link-inherit" );
-
- countParent.addClass( "ui-li-jsnumbering" )
- .prepend( "<span class='ui-li-dec'>" + (counter++) + ". </span>" );
- }
-
- item.add( item.children( ".ui-btn-inner" ) ).addClass( itemClass );
-
- self._itemApply( $list, item );
- }
-
- this._refreshCorners( create );
- },
-
- //create a string for ID/subpage url creation
- _idStringEscape: function( str ) {
- return str.replace(/[^a-zA-Z0-9]/g, '-');
- },
-
- _createSubPages: function() {
- var parentList = this.element,
- parentPage = parentList.closest( ".ui-page" ),
- parentUrl = parentPage.jqmData( "url" ),
- parentId = parentUrl || parentPage[ 0 ][ $.expando ],
- parentListId = parentList.attr( "id" ),
- o = this.options,
- dns = "data-" + $.mobile.ns,
- self = this,
- persistentFooterID = parentPage.find( ":jqmData(role='footer')" ).jqmData( "id" ),
- hasSubPages;
-
- if ( typeof listCountPerPage[ parentId ] === "undefined" ) {
- listCountPerPage[ parentId ] = -1;
- }
-
- parentListId = parentListId || ++listCountPerPage[ parentId ];
-
- $( parentList.find( "li>ul, li>ol" ).toArray().reverse() ).each(function( i ) {
- var self = this,
- list = $( this ),
- listId = list.attr( "id" ) || parentListId + "-" + i,
- parent = list.parent(),
- nodeEls = $( list.prevAll().toArray().reverse() ),
- nodeEls = nodeEls.length ? nodeEls : $( "<span>" + $.trim(parent.contents()[ 0 ].nodeValue) + "</span>" ),
- title = nodeEls.first().text(),//url limits to first 30 chars of text
- id = ( parentUrl || "" ) + "&" + $.mobile.subPageUrlKey + "=" + listId,
- theme = list.jqmData( "theme" ) || o.theme,
- countTheme = list.jqmData( "counttheme" ) || parentList.jqmData( "counttheme" ) || o.countTheme,
- newPage, anchor;
-
- //define hasSubPages for use in later removal
- hasSubPages = true;
-
- newPage = list.detach()
- .wrap( "<div " + dns + "role='page' " + dns + "url='" + id + "' " + dns + "theme='" + theme + "' " + dns + "count-theme='" + countTheme + "'><div " + dns + "role='content'></div></div>" )
- .parent()
- .before( "<div " + dns + "role='header' " + dns + "theme='" + o.headerTheme + "'><div class='ui-title'>" + title + "</div></div>" )
- .after( persistentFooterID ? $( "<div " + dns + "role='footer' " + dns + "id='"+ persistentFooterID +"'>") : "" )
- .parent()
- .appendTo( $.mobile.pageContainer );
-
- newPage.page();
-
- anchor = parent.find('a:first');
-
- if ( !anchor.length ) {
- anchor = $( "<a/>" ).html( nodeEls || title ).prependTo( parent.empty() );
- }
-
- anchor.attr( "href", "#" + id );
-
- }).listview();
-
- //on pagehide, remove any nested pages along with the parent page, as long as they aren't active
- if( hasSubPages && parentPage.data("page").options.domCache === false ){
- var newRemove = function( e, ui ){
- var nextPage = ui.nextPage, npURL;
-
- if( ui.nextPage ){
- npURL = nextPage.jqmData( "url" );
- if( npURL.indexOf( parentUrl + "&" + $.mobile.subPageUrlKey ) !== 0 ){
- self.childPages().remove();
- parentPage.remove();
- }
- }
- };
-
- // unbind the original page remove and replace with our specialized version
- parentPage
- .unbind( "pagehide.remove" )
- .bind( "pagehide.remove", newRemove);
- }
- },
-
- // TODO sort out a better way to track sub pages of the listview this is brittle
- childPages: function(){
- var parentUrl = this.parentPage.jqmData( "url" );
-
- return $( ":jqmData(url^='"+ parentUrl + "&" + $.mobile.subPageUrlKey +"')");
- }
-});
-
-//auto self-init widgets
-$( document ).bind( "pagecreate create", function( e ){
- $( $.mobile.listview.prototype.options.initSelector, e.target ).listview();
-});
-
-})( jQuery );
-/*
-* jQuery Mobile Framework : "listview" filter extension
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-
-(function( $, undefined ) {
-
-$.mobile.listview.prototype.options.filter = false;
-$.mobile.listview.prototype.options.filterPlaceholder = "Filter items...";
-$.mobile.listview.prototype.options.filterTheme = "c";
-$.mobile.listview.prototype.options.filterCallback = function( text, searchValue ){
- return text.toLowerCase().indexOf( searchValue ) === -1;
-};
-
-$( ":jqmData(role='listview')" ).live( "listviewcreate", function() {
-
- var list = $( this ),
- listview = list.data( "listview" );
-
- if ( !listview.options.filter ) {
- return;
- }
-
- var wrapper = $( "<form>", {
- "class": "ui-listview-filter ui-bar-" + listview.options.filterTheme,
- "role": "search"
- }),
- search = $( "<input>", {
- placeholder: listview.options.filterPlaceholder
- })
- .attr( "data-" + $.mobile.ns + "type", "search" )
- .jqmData( "lastval", "" )
- .bind( "keyup change", function() {
-
- var $this = $(this),
- val = this.value.toLowerCase(),
- listItems = null,
- lastval = $this.jqmData( "lastval" ) + "",
- childItems = false,
- itemtext = "",
- item, change;
-
- // Change val as lastval for next execution
- $this.jqmData( "lastval" , val );
-
- change = val.replace( new RegExp( "^" + lastval ) , "" );
-
- if ( val.length < lastval.length || change.length != ( val.length - lastval.length ) ) {
-
- // Removed chars or pasted something totaly different, check all items
- listItems = list.children();
- } else {
-
- // Only chars added, not removed, only use visible subset
- listItems = list.children( ":not(.ui-screen-hidden)" );
- }
-
- if ( val ) {
-
- // This handles hiding regular rows without the text we search for
- // and any list dividers without regular rows shown under it
-
- for ( var i = listItems.length - 1; i >= 0; i-- ) {
- item = $( listItems[ i ] );
- itemtext = item.jqmData( "filtertext" ) || item.text();
-
- if ( item.is( "li:jqmData(role=list-divider)" ) ) {
-
- item.toggleClass( "ui-filter-hidequeue" , !childItems );
-
- // New bucket!
- childItems = false;
-
- } else if ( listview.options.filterCallback( itemtext, val ) ) {
-
- //mark to be hidden
- item.toggleClass( "ui-filter-hidequeue" , true );
- } else {
-
- // There"s a shown item in the bucket
- childItems = true;
- }
- }
-
- // Show items, not marked to be hidden
- listItems
- .filter( ":not(.ui-filter-hidequeue)" )
- .toggleClass( "ui-screen-hidden", false );
-
- // Hide items, marked to be hidden
- listItems
- .filter( ".ui-filter-hidequeue" )
- .toggleClass( "ui-screen-hidden", true )
- .toggleClass( "ui-filter-hidequeue", false );
-
- } else {
-
- //filtervalue is empty => show all
- listItems.toggleClass( "ui-screen-hidden", false );
- }
- listview._refreshCorners();
- })
- .appendTo( wrapper )
- .textinput();
-
- if ( $( this ).jqmData( "inset" ) ) {
- wrapper.addClass( "ui-listview-filter-inset" );
- }
-
- wrapper.bind( "submit", function() {
- return false;
- })
- .insertBefore( list );
-});
-
-})( jQuery );/*
-* jQuery Mobile Framework : "fieldcontain" plugin - simple class additions to make form row separators
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-
-(function( $, undefined ) {
-
-$( document ).bind( "pagecreate create", function( e ){
- $( ":jqmData(role='nojs')", e.target ).addClass( "ui-nojs" );
-
-});
-
-})( jQuery );/*
-* jQuery Mobile Framework : "checkboxradio" plugin
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-
-(function( $, undefined ) {
-
-$.widget( "mobile.checkboxradio", $.mobile.widget, {
- options: {
- theme: null,
- initSelector: "input[type='checkbox'],input[type='radio']"
- },
- _create: function() {
- var self = this,
- input = this.element,
- // NOTE: Windows Phone could not find the label through a selector
- // filter works though.
- label = input.closest( "form,fieldset,:jqmData(role='page')" ).find( "label" ).filter( "[for='" + input[ 0 ].id + "']"),
- inputtype = input.attr( "type" ),
- checkedState = inputtype + "-on",
- uncheckedState = inputtype + "-off",
- icon = input.parents( ":jqmData(type='horizontal')" ).length ? undefined : uncheckedState,
- activeBtn = icon ? "" : " " + $.mobile.activeBtnClass,
- checkedClass = "ui-" + checkedState + activeBtn,
- uncheckedClass = "ui-" + uncheckedState,
- checkedicon = "ui-icon-" + checkedState,
- uncheckedicon = "ui-icon-" + uncheckedState;
-
- if ( inputtype !== "checkbox" && inputtype !== "radio" ) {
- return;
- }
-
- // Expose for other methods
- $.extend( this, {
- label: label,
- inputtype: inputtype,
- checkedClass: checkedClass,
- uncheckedClass: uncheckedClass,
- checkedicon: checkedicon,
- uncheckedicon: uncheckedicon
- });
-
- // If there's no selected theme...
- if( !this.options.theme ) {
- this.options.theme = this.element.jqmData( "theme" );
- }
-
- label.buttonMarkup({
- theme: this.options.theme,
- icon: icon,
- shadow: false
- });
-
- // Wrap the input + label in a div
- input.add( label )
- .wrapAll( "<div class='ui-" + inputtype + "'></div>" );
-
- label.bind({
- vmouseover: function() {
- if ( $( this ).parent().is( ".ui-disabled" ) ) {
- return false;
- }
- },
-
- vclick: function( event ) {
- if ( input.is( ":disabled" ) ) {
- event.preventDefault();
- return;
- }
-
- self._cacheVals();
-
- input.prop( "checked", inputtype === "radio" && true || !input.prop( "checked" ) );
-
- // Input set for common radio buttons will contain all the radio
- // buttons, but will not for checkboxes. clearing the checked status
- // of other radios ensures the active button state is applied properly
- self._getInputSet().not( input ).prop( "checked", false );
-
- self._updateAll();
- return false;
- }
-
- });
-
- input
- .bind({
- vmousedown: function() {
- this._cacheVals();
- },
-
- vclick: function() {
-
- var $this = $(this);
-
- // Adds checked attribute to checked input when keyboard is used
- if ( $this.is( ":checked" ) ) {
-
- $this.prop( "checked", true);
- self._getInputSet().not($this).prop( "checked", false );
- } else {
-
- $this.prop( "checked", false );
- }
-
- self._updateAll();
- },
-
- focus: function() {
- label.addClass( "ui-focus" );
- },
-
- blur: function() {
- label.removeClass( "ui-focus" );
- }
- });
-
- this.refresh();
- },
-
- _cacheVals: function() {
- this._getInputSet().each(function() {
- var $this = $(this);
-
- $this.jqmData( "cacheVal", $this.is( ":checked" ) );
- });
- },
-
- //returns either a set of radios with the same name attribute, or a single checkbox
- _getInputSet: function(){
- if(this.inputtype == "checkbox") {
- return this.element;
- }
- return this.element.closest( "form,fieldset,:jqmData(role='page')" )
- .find( "input[name='"+ this.element.attr( "name" ) +"'][type='"+ this.inputtype +"']" );
- },
-
- _updateAll: function() {
- var self = this;
-
- this._getInputSet().each(function() {
- var $this = $(this);
-
- if ( $this.is( ":checked" ) || self.inputtype === "checkbox" ) {
- $this.trigger( "change" );
- }
- })
- .checkboxradio( "refresh" );
- },
-
- refresh: function() {
- var input = this.element,
- label = this.label,
- icon = label.find( ".ui-icon" );
-
- // input[0].checked expando doesn't always report the proper value
- // for checked='checked'
- if ( $( input[ 0 ] ).prop( "checked" ) ) {
-
- label.addClass( this.checkedClass ).removeClass( this.uncheckedClass );
- icon.addClass( this.checkedicon ).removeClass( this.uncheckedicon );
-
- } else {
-
- label.removeClass( this.checkedClass ).addClass( this.uncheckedClass );
- icon.removeClass( this.checkedicon ).addClass( this.uncheckedicon );
- }
-
- if ( input.is( ":disabled" ) ) {
- this.disable();
- } else {
- this.enable();
- }
- },
-
- disable: function() {
- this.element.prop( "disabled", true ).parent().addClass( "ui-disabled" );
- },
-
- enable: function() {
- this.element.prop( "disabled", false ).parent().removeClass( "ui-disabled" );
- }
-});
-
-//auto self-init widgets
-$( document ).bind( "pagecreate create", function( e ){
- $( $.mobile.checkboxradio.prototype.options.initSelector, e.target )
- .not( ":jqmData(role='none'), :jqmData(role='nojs')" )
- .checkboxradio();
-});
-
-})( jQuery );
-/*
-* jQuery Mobile Framework : "button" plugin - links that proxy to native input/buttons
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-
-(function( $, undefined ) {
-
-$.widget( "mobile.button", $.mobile.widget, {
- options: {
- theme: null,
- icon: null,
- iconpos: null,
- inline: null,
- corners: true,
- shadow: true,
- iconshadow: true,
- initSelector: "button, [type='button'], [type='submit'], [type='reset'], [type='image']"
- },
- _create: function() {
- var $el = this.element,
- o = this.options,
- type;
-
- // Add ARIA role
- this.button = $( "<div></div>" )
- .text( $el.text() || $el.val() )
- .buttonMarkup({
- theme: o.theme,
- icon: o.icon,
- iconpos: o.iconpos,
- inline: o.inline,
- corners: o.corners,
- shadow: o.shadow,
- iconshadow: o.iconshadow
- })
- .insertBefore( $el )
- .append( $el.addClass( "ui-btn-hidden" ) );
-
- // Add hidden input during submit
- type = $el.attr( "type" );
-
- if ( type !== "button" && type !== "reset" ) {
-
- $el.bind( "vclick", function() {
-
- var $buttonPlaceholder = $( "<input>", {
- type: "hidden",
- name: $el.attr( "name" ),
- value: $el.attr( "value" )
- })
- .insertBefore( $el );
-
- // Bind to doc to remove after submit handling
- $( document ).submit(function(){
- $buttonPlaceholder.remove();
- });
- });
- }
-
- this.refresh();
- },
-
- enable: function() {
- this.element.attr( "disabled", false );
- this.button.removeClass( "ui-disabled" ).attr( "aria-disabled", false );
- return this._setOption( "disabled", false );
- },
-
- disable: function() {
- this.element.attr( "disabled", true );
- this.button.addClass( "ui-disabled" ).attr( "aria-disabled", true );
- return this._setOption( "disabled", true );
- },
-
- refresh: function() {
- if ( this.element.attr( "disabled" ) ) {
- this.disable();
- } else {
- this.enable();
- }
- }
-});
-
-//auto self-init widgets
-$( document ).bind( "pagecreate create", function( e ){
- $( $.mobile.button.prototype.options.initSelector, e.target )
- .not( ":jqmData(role='none'), :jqmData(role='nojs')" )
- .button();
-});
-
-})( jQuery );/*
-* jQuery Mobile Framework : "slider" plugin
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-
-( function( $, undefined ) {
-
-$.widget( "mobile.slider", $.mobile.widget, {
- options: {
- theme: null,
- trackTheme: null,
- disabled: false,
- initSelector: "input[type='range'], :jqmData(type='range'), :jqmData(role='slider')"
- },
-
- _create: function() {
-
- // TODO: Each of these should have comments explain what they're for
- var self = this,
-
- control = this.element,
-
- parentTheme = control.parents( "[class*='ui-bar-'],[class*='ui-body-']" ).eq( 0 ),
-
- parentTheme = parentTheme.length ? parentTheme.attr( "class" ).match( /ui-(bar|body)-([a-z])/ )[ 2 ] : "c",
-
- theme = this.options.theme ? this.options.theme : parentTheme,
-
- trackTheme = this.options.trackTheme ? this.options.trackTheme : parentTheme,
-
- cType = control[ 0 ].nodeName.toLowerCase(),
-
- selectClass = ( cType == "select" ) ? "ui-slider-switch" : "",
-
- controlID = control.attr( "id" ),
-
- labelID = controlID + "-label",
-
- label = $( "[for='"+ controlID +"']" ).attr( "id", labelID ),
-
- val = function() {
- return cType == "input" ? parseFloat( control.val() ) : control[0].selectedIndex;
- },
-
- min = cType == "input" ? parseFloat( control.attr( "min" ) ) : 0,
-
- max = cType == "input" ? parseFloat( control.attr( "max" ) ) : control.find( "option" ).length-1,
-
- step = window.parseFloat( control.attr( "step" ) || 1 ),
-
- slider = $( "<div class='ui-slider " + selectClass + " ui-btn-down-" + trackTheme +
- " ui-btn-corner-all' role='application'></div>" ),
-
- handle = $( "<a href='#' class='ui-slider-handle'></a>" )
- .appendTo( slider )
- .buttonMarkup({ corners: true, theme: theme, shadow: true })
- .attr({
- "role": "slider",
- "aria-valuemin": min,
- "aria-valuemax": max,
- "aria-valuenow": val(),
- "aria-valuetext": val(),
- "title": val(),
- "aria-labelledby": labelID
- }),
- options;
-
- $.extend( this, {
- slider: slider,
- handle: handle,
- dragging: false,
- beforeStart: null,
- userModified: false
- });
-
- if ( cType == "select" ) {
-
- slider.wrapInner( "<div class='ui-slider-inneroffset'></div>" );
-
- options = control.find( "option" );
-
- control.find( "option" ).each(function( i ) {
-
- var side = !i ? "b":"a",
- corners = !i ? "right" :"left",
- theme = !i ? " ui-btn-down-" + trackTheme :( " " + $.mobile.activeBtnClass );
-
- $( "<div class='ui-slider-labelbg ui-slider-labelbg-" + side + theme + " ui-btn-corner-" + corners + "'></div>" )
- .prependTo( slider );
-
- $( "<span class='ui-slider-label ui-slider-label-" + side + theme + " ui-btn-corner-" + corners + "' role='img'>" + $( this ).text() + "</span>" )
- .prependTo( handle );
- });
-
- }
-
- label.addClass( "ui-slider" );
-
- // monitor the input for updated values
- control.addClass( cType === "input" ? "ui-slider-input" : "ui-slider-switch" )
- .change( function() {
- self.refresh( val(), true );
- })
- .keyup( function() { // necessary?
- self.refresh( val(), true, true );
- })
- .blur( function() {
- self.refresh( val(), true );
- });
-
- // prevent screen drag when slider activated
- $( document ).bind( "vmousemove", function( event ) {
- if ( self.dragging ) {
- self.refresh( event );
- self.userModified = self.userModified || self.beforeStart !== control[0].selectedIndex;
- return false;
- }
- });
-
- slider.bind( "vmousedown", function( event ) {
- self.dragging = true;
- self.userModified = false;
-
- if ( cType === "select" ) {
- self.beforeStart = control[0].selectedIndex;
- }
- self.refresh( event );
- return false;
- });
-
- slider.add( document )
- .bind( "vmouseup", function() {
- if ( self.dragging ) {
-
- self.dragging = false;
-
- if ( cType === "select" ) {
-
- if ( !self.userModified ) {
- //tap occurred, but value didn't change. flip it!
- handle.addClass( "ui-slider-handle-snapping" );
- self.refresh( !self.beforeStart ? 1 : 0 );
- }
- }
- return false;
- }
- });
-
- slider.insertAfter( control );
-
- // NOTE force focus on handle
- this.handle
- .bind( "vmousedown", function() {
- $( this ).focus();
- })
- .bind( "vclick", false );
-
- this.handle
- .bind( "keydown", function( event ) {
- var index = val();
-
- if ( self.options.disabled ) {
- return;
- }
-
- // In all cases prevent the default and mark the handle as active
- switch ( event.keyCode ) {
- case $.mobile.keyCode.HOME:
- case $.mobile.keyCode.END:
- case $.mobile.keyCode.PAGE_UP:
- case $.mobile.keyCode.PAGE_DOWN:
- case $.mobile.keyCode.UP:
- case $.mobile.keyCode.RIGHT:
- case $.mobile.keyCode.DOWN:
- case $.mobile.keyCode.LEFT:
- event.preventDefault();
-
- if ( !self._keySliding ) {
- self._keySliding = true;
- $( this ).addClass( "ui-state-active" );
- }
- break;
- }
-
- // move the slider according to the keypress
- switch ( event.keyCode ) {
- case $.mobile.keyCode.HOME:
- self.refresh( min );
- break;
- case $.mobile.keyCode.END:
- self.refresh( max );
- break;
- case $.mobile.keyCode.PAGE_UP:
- case $.mobile.keyCode.UP:
- case $.mobile.keyCode.RIGHT:
- self.refresh( index + step );
- break;
- case $.mobile.keyCode.PAGE_DOWN:
- case $.mobile.keyCode.DOWN:
- case $.mobile.keyCode.LEFT:
- self.refresh( index - step );
- break;
- }
- }) // remove active mark
- .keyup( function( event ) {
- if ( self._keySliding ) {
- self._keySliding = false;
- $( this ).removeClass( "ui-state-active" );
- }
- });
-
- this.refresh(undefined, undefined, true);
- },
-
- refresh: function( val, isfromControl, preventInputUpdate ) {
- if ( this.options.disabled ) { return; }
-
- var control = this.element, percent,
- cType = control[0].nodeName.toLowerCase(),
- min = cType === "input" ? parseFloat( control.attr( "min" ) ) : 0,
- max = cType === "input" ? parseFloat( control.attr( "max" ) ) : control.find( "option" ).length - 1;
-
- if ( typeof val === "object" ) {
- var data = val,
- // a slight tolerance helped get to the ends of the slider
- tol = 8;
- if ( !this.dragging ||
- data.pageX < this.slider.offset().left - tol ||
- data.pageX > this.slider.offset().left + this.slider.width() + tol ) {
- return;
- }
- percent = Math.round( ( ( data.pageX - this.slider.offset().left ) / this.slider.width() ) * 100 );
- } else {
- if ( val == null ) {
- val = cType === "input" ? parseFloat( control.val() ) : control[0].selectedIndex;
- }
- percent = ( parseFloat( val ) - min ) / ( max - min ) * 100;
- }
-
- if ( isNaN( percent ) ) {
- return;
- }
-
- if ( percent < 0 ) {
- percent = 0;
- }
-
- if ( percent > 100 ) {
- percent = 100;
- }
-
- var newval = Math.round( ( percent / 100 ) * ( max - min ) ) + min;
-
- if ( newval < min ) {
- newval = min;
- }
-
- if ( newval > max ) {
- newval = max;
- }
-
- // Flip the stack of the bg colors
- if ( percent > 60 && cType === "select" ) {
- // TODO: Dead path?
- }
- this.handle.css( "left", percent + "%" );
- this.handle.attr( {
- "aria-valuenow": cType === "input" ? newval : control.find( "option" ).eq( newval ).attr( "value" ),
- "aria-valuetext": cType === "input" ? newval : control.find( "option" ).eq( newval ).text(),
- title: newval
- });
-
- // add/remove classes for flip toggle switch
- if ( cType === "select" ) {
- if ( newval === 0 ) {
- this.slider.addClass( "ui-slider-switch-a" )
- .removeClass( "ui-slider-switch-b" );
- } else {
- this.slider.addClass( "ui-slider-switch-b" )
- .removeClass( "ui-slider-switch-a" );
- }
- }
-
- if ( !preventInputUpdate ) {
- // update control"s value
- if ( cType === "input" ) {
- control.val( newval );
- } else {
- control[ 0 ].selectedIndex = newval;
- }
- if ( !isfromControl ) {
- control.trigger( "change" );
- }
- }
- },
-
- enable: function() {
- this.element.attr( "disabled", false );
- this.slider.removeClass( "ui-disabled" ).attr( "aria-disabled", false );
- return this._setOption( "disabled", false );
- },
-
- disable: function() {
- this.element.attr( "disabled", true );
- this.slider.addClass( "ui-disabled" ).attr( "aria-disabled", true );
- return this._setOption( "disabled", true );
- }
-
-});
-
-//auto self-init widgets
-$( document ).bind( "pagecreate create", function( e ){
-
- $( $.mobile.slider.prototype.options.initSelector, e.target )
- .not( ":jqmData(role='none'), :jqmData(role='nojs')" )
- .slider();
-
-});
-
-})( jQuery );
-/*
-* jQuery Mobile Framework : "textinput" plugin for text inputs, textareas
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-
-(function( $, undefined ) {
-
-$.widget( "mobile.textinput", $.mobile.widget, {
- options: {
- theme: null,
- initSelector: "input[type='text'], input[type='search'], :jqmData(type='search'), input[type='number'], :jqmData(type='number'), input[type='password'], input[type='email'], input[type='url'], input[type='tel'], textarea"
- },
-
- _create: function() {
-
- var input = this.element,
- o = this.options,
- theme = o.theme,
- themedParent, themeclass, themeLetter, focusedEl, clearbtn;
-
- if ( !theme ) {
- themedParent = this.element.closest( "[class*='ui-bar-'],[class*='ui-body-']" );
- themeLetter = themedParent.length && /ui-(bar|body)-([a-z])/.exec( themedParent.attr( "class" ) );
- theme = themeLetter && themeLetter[2] || "c";
- }
-
- themeclass = " ui-body-" + theme;
-
- $( "label[for='" + input.attr( "id" ) + "']" ).addClass( "ui-input-text" );
-
- input.addClass("ui-input-text ui-body-"+ o.theme );
-
- focusedEl = input;
-
- // XXX: Temporary workaround for issue 785. Turn off autocorrect and
- // autocomplete since the popup they use can't be dismissed by
- // the user. Note that we test for the presence of the feature
- // by looking for the autocorrect property on the input element.
- if ( typeof input[0].autocorrect !== "undefined" ) {
- // Set the attribute instead of the property just in case there
- // is code that attempts to make modifications via HTML.
- input[0].setAttribute( "autocorrect", "off" );
- input[0].setAttribute( "autocomplete", "off" );
- }
-
-
- //"search" input widget
- if ( input.is( "[type='search'],:jqmData(type='search')" ) ) {
-
- focusedEl = input.wrap( "<div class='ui-input-search ui-shadow-inset ui-btn-corner-all ui-btn-shadow ui-icon-searchfield" + themeclass + "'></div>" ).parent();
- clearbtn = $( "<a href='#' class='ui-input-clear' title='clear text'>clear text</a>" )
- .tap(function( event ) {
- input.val( "" ).focus();
- input.trigger( "change" );
- clearbtn.addClass( "ui-input-clear-hidden" );
- event.preventDefault();
- })
- .appendTo( focusedEl )
- .buttonMarkup({
- icon: "delete",
- iconpos: "notext",
- corners: true,
- shadow: true
- });
-
- function toggleClear() {
- if ( !input.val() ) {
- clearbtn.addClass( "ui-input-clear-hidden" );
- } else {
- clearbtn.removeClass( "ui-input-clear-hidden" );
- }
- }
-
- toggleClear();
-
- input.keyup( toggleClear )
- .focus( toggleClear );
-
- } else {
- input.addClass( "ui-corner-all ui-shadow-inset" + themeclass );
- }
-
- input.focus(function() {
- focusedEl.addClass( "ui-focus" );
- })
- .blur(function(){
- focusedEl.removeClass( "ui-focus" );
- });
-
- // Autogrow
- if ( input.is( "textarea" ) ) {
- var extraLineHeight = 15,
- keyupTimeoutBuffer = 100,
- keyup = function() {
- var scrollHeight = input[ 0 ].scrollHeight,
- clientHeight = input[ 0 ].clientHeight;
-
- if ( clientHeight < scrollHeight ) {
- input.css({
- height: (scrollHeight + extraLineHeight)
- });
- }
- },
- keyupTimeout;
-
- input.keyup(function() {
- clearTimeout( keyupTimeout );
- keyupTimeout = setTimeout( keyup, keyupTimeoutBuffer );
- });
- }
- },
-
- disable: function(){
- ( this.element.attr( "disabled", true ).is( "[type='search'],:jqmData(type='search')" ) ?
- this.element.parent() : this.element ).addClass( "ui-disabled" );
- },
-
- enable: function(){
- ( this.element.attr( "disabled", false).is( "[type='search'],:jqmData(type='search')" ) ?
- this.element.parent() : this.element ).removeClass( "ui-disabled" );
- }
-});
-
-//auto self-init widgets
-$( document ).bind( "pagecreate create", function( e ){
-
- $( $.mobile.textinput.prototype.options.initSelector, e.target )
- .not( ":jqmData(role='none'), :jqmData(role='nojs')" )
- .textinput();
-
-});
-
-})( jQuery );
-/*
-* jQuery Mobile Framework : custom "selectmenu" plugin
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-
-(function( $, undefined ) {
- var extendSelect = function( widget ){
-
- var select = widget.select,
- selectID = widget.selectID,
- label = widget.label,
- thisPage = widget.select.closest( ".ui-page" ),
- screen = $( "<div>", {"class": "ui-selectmenu-screen ui-screen-hidden"} ).appendTo( thisPage ),
- selectOptions = widget.select.find("option"),
- isMultiple = widget.isMultiple = widget.select[ 0 ].multiple,
- buttonId = selectID + "-button",
- menuId = selectID + "-menu",
- menuPage = $( "<div data-" + $.mobile.ns + "role='dialog' data-" +$.mobile.ns + "theme='"+ widget.options.menuPageTheme +"'>" +
- "<div data-" + $.mobile.ns + "role='header'>" +
- "<div class='ui-title'>" + label.text() + "</div>"+
- "</div>"+
- "<div data-" + $.mobile.ns + "role='content'></div>"+
- "</div>" ).appendTo( $.mobile.pageContainer ).page(),
-
- listbox = $("<div>", { "class": "ui-selectmenu ui-selectmenu-hidden ui-overlay-shadow ui-corner-all ui-body-" + widget.options.overlayTheme + " " + $.mobile.defaultDialogTransition } ).insertAfter(screen),
-
- list = $( "<ul>", {
- "class": "ui-selectmenu-list",
- "id": menuId,
- "role": "listbox",
- "aria-labelledby": buttonId
- }).attr( "data-" + $.mobile.ns + "theme", widget.options.theme ).appendTo( listbox ),
-
- header = $( "<div>", {
- "class": "ui-header ui-bar-" + widget.options.theme
- }).prependTo( listbox ),
-
- headerTitle = $( "<h1>", {
- "class": "ui-title"
- }).appendTo( header ),
-
- headerClose = $( "<a>", {
- "text": widget.options.closeText,
- "href": "#",
- "class": "ui-btn-left"
- }).attr( "data-" + $.mobile.ns + "iconpos", "notext" ).attr( "data-" + $.mobile.ns + "icon", "delete" ).appendTo( header ).buttonMarkup(),
-
- menuPageContent = menuPage.find( ".ui-content" ),
-
- menuPageClose = menuPage.find( ".ui-header a" );
-
-
- $.extend( widget, {
- select: widget.select,
- selectID: selectID,
- buttonId: buttonId,
- menuId: menuId,
- thisPage: thisPage,
- menuPage: menuPage,
- label: label,
- screen: screen,
- selectOptions: selectOptions,
- isMultiple: isMultiple,
- theme: widget.options.theme,
- listbox: listbox,
- list: list,
- header: header,
- headerTitle: headerTitle,
- headerClose: headerClose,
- menuPageContent: menuPageContent,
- menuPageClose: menuPageClose,
- placeholder: "",
-
- build: function() {
- var self = this;
-
- // Create list from select, update state
- self.refresh();
-
- self.select.attr( "tabindex", "-1" ).focus(function() {
- $( this ).blur();
- self.button.focus();
- });
-
- // Button events
- self.button.bind( "vclick keydown" , function( event ) {
- if ( event.type == "vclick" ||
- event.keyCode && ( event.keyCode === $.mobile.keyCode.ENTER ||
- event.keyCode === $.mobile.keyCode.SPACE ) ) {
-
- self.open();
- event.preventDefault();
- }
- });
-
- // Events for list items
- self.list.attr( "role", "listbox" )
- .delegate( ".ui-li>a", "focusin", function() {
- $( this ).attr( "tabindex", "0" );
- })
- .delegate( ".ui-li>a", "focusout", function() {
- $( this ).attr( "tabindex", "-1" );
- })
- .delegate( "li:not(.ui-disabled, .ui-li-divider)", "click", function( event ) {
-
- // index of option tag to be selected
- var oldIndex = self.select[ 0 ].selectedIndex,
- newIndex = self.list.find( "li:not(.ui-li-divider)" ).index( this ),
- option = self.selectOptions.eq( newIndex )[ 0 ];
-
- // toggle selected status on the tag for multi selects
- option.selected = self.isMultiple ? !option.selected : true;
-
- // toggle checkbox class for multiple selects
- if ( self.isMultiple ) {
- $( this ).find( ".ui-icon" )
- .toggleClass( "ui-icon-checkbox-on", option.selected )
- .toggleClass( "ui-icon-checkbox-off", !option.selected );
- }
-
- // trigger change if value changed
- if ( self.isMultiple || oldIndex !== newIndex ) {
- self.select.trigger( "change" );
- }
-
- //hide custom select for single selects only
- if ( !self.isMultiple ) {
- self.close();
- }
-
- event.preventDefault();
- })
- .keydown(function( event ) { //keyboard events for menu items
- var target = $( event.target ),
- li = target.closest( "li" ),
- prev, next;
-
- // switch logic based on which key was pressed
- switch ( event.keyCode ) {
- // up or left arrow keys
- case 38:
- prev = li.prev();
-
- // if there's a previous option, focus it
- if ( prev.length ) {
- target
- .blur()
- .attr( "tabindex", "-1" );
-
- prev.find( "a" ).first().focus();
- }
-
- return false;
- break;
-
- // down or right arrow keys
- case 40:
- next = li.next();
-
- // if there's a next option, focus it
- if ( next.length ) {
- target
- .blur()
- .attr( "tabindex", "-1" );
-
- next.find( "a" ).first().focus();
- }
-
- return false;
- break;
-
- // If enter or space is pressed, trigger click
- case 13:
- case 32:
- target.trigger( "click" );
-
- return false;
- break;
- }
- });
-
- // button refocus ensures proper height calculation
- // by removing the inline style and ensuring page inclusion
- self.menuPage.bind( "pagehide", function() {
- self.list.appendTo( self.listbox );
- self._focusButton();
- });
-
- // Events on "screen" overlay
- self.screen.bind( "vclick", function( event ) {
- self.close();
- });
-
- // Close button on small overlays
- self.headerClose.click( function() {
- if ( self.menuType == "overlay" ) {
- self.close();
- return false;
- }
- });
- },
-
- refresh: function( forceRebuild ){
- var self = this,
- select = this.element,
- isMultiple = this.isMultiple,
- options = this.selectOptions = select.find( "option" ),
- selected = this.selected(),
- // return an array of all selected index's
- indicies = this.selectedIndices();
-
- if ( forceRebuild || select[0].options.length != self.list.find( "li" ).length ) {
- self._buildList();
- }
-
- self.setButtonText();
- self.setButtonCount();
-
- self.list.find( "li:not(.ui-li-divider)" )
- .removeClass( $.mobile.activeBtnClass )
- .attr( "aria-selected", false )
- .each(function( i ) {
-
- if ( $.inArray( i, indicies ) > -1 ) {
- var item = $( this );
-
- // Aria selected attr
- item.attr( "aria-selected", true );
-
- // Multiple selects: add the "on" checkbox state to the icon
- if ( self.isMultiple ) {
- item.find( ".ui-icon" ).removeClass( "ui-icon-checkbox-off" ).addClass( "ui-icon-checkbox-on" );
- } else {
- item.addClass( $.mobile.activeBtnClass );
- }
- }
- });
- },
-
- close: function() {
- if ( this.options.disabled || !this.isOpen ) {
- return;
- }
-
- var self = this;
-
- if ( self.menuType == "page" ) {
- // TODO centralize page removal binding / handling in the page plugin.
- // Suggestion from @jblas to do refcounting
- //
- // rebind the page remove that was unbound in the open function
- // to allow for the parent page removal from actions other than the use
- // of a dialog sized custom select
- if( !self.thisPage.data("page").options.domCache ){
- self.thisPage.bind( "pagehide.remove", function() {
- $(self).remove();
- });
- }
-
- // doesn't solve the possible issue with calling change page
- // where the objects don't define data urls which prevents dialog key
- // stripping - changePage has incoming refactor
- window.history.back();
- } else {
- self.screen.addClass( "ui-screen-hidden" );
- self.listbox.addClass( "ui-selectmenu-hidden" ).removeAttr( "style" ).removeClass( "in" );
- self.list.appendTo( self.listbox );
- self._focusButton();
- }
-
- // allow the dialog to be closed again
- self.isOpen = false;
- },
-
- open: function() {
- if ( this.options.disabled ) {
- return;
- }
-
- var self = this,
- menuHeight = self.list.parent().outerHeight(),
- menuWidth = self.list.parent().outerWidth(),
- scrollTop = $( window ).scrollTop(),
- btnOffset = self.button.offset().top,
- screenHeight = window.innerHeight,
- screenWidth = window.innerWidth;
-
- //add active class to button
- self.button.addClass( $.mobile.activeBtnClass );
-
- //remove after delay
- setTimeout( function() {
- self.button.removeClass( $.mobile.activeBtnClass );
- }, 300);
-
- function focusMenuItem() {
- self.list.find( $.mobile.activeBtnClass ).focus();
- }
-
- if ( menuHeight > screenHeight - 80 || !$.support.scrollTop ) {
- // prevent the parent page from being removed from the DOM,
- // otherwise the results of selecting a list item in the dialog
- // fall into a black hole
- self.thisPage.unbind( "pagehide.remove" );
-
- //for webos (set lastscroll using button offset)
- if ( scrollTop == 0 && btnOffset > screenHeight ) {
- self.thisPage.one( "pagehide", function() {
- $( this ).jqmData( "lastScroll", btnOffset );
- });
- }
-
- self.menuPage.one( "pageshow", function() {
- // silentScroll() is called whenever a page is shown to restore
- // any previous scroll position the page may have had. We need to
- // wait for the "silentscroll" event before setting focus to avoid
- // the browser"s "feature" which offsets rendering to make sure
- // whatever has focus is in view.
- $( window ).one( "silentscroll", function() {
- focusMenuItem();
- });
-
- self.isOpen = true;
- });
-
- self.menuType = "page";
- self.menuPageContent.append( self.list );
- $.mobile.changePage( self.menuPage, {
- transition: $.mobile.defaultDialogTransition
- });
- } else {
- self.menuType = "overlay";
-
- self.screen.height( $(document).height() )
- .removeClass( "ui-screen-hidden" );
-
- // Try and center the overlay over the button
- var roomtop = btnOffset - scrollTop,
- roombot = scrollTop + screenHeight - btnOffset,
- halfheight = menuHeight / 2,
- maxwidth = parseFloat( self.list.parent().css( "max-width" ) ),
- newtop, newleft;
-
- if ( roomtop > menuHeight / 2 && roombot > menuHeight / 2 ) {
- newtop = btnOffset + ( self.button.outerHeight() / 2 ) - halfheight;
- } else {
- // 30px tolerance off the edges
- newtop = roomtop > roombot ? scrollTop + screenHeight - menuHeight - 30 : scrollTop + 30;
- }
-
- // If the menuwidth is smaller than the screen center is
- if ( menuWidth < maxwidth ) {
- newleft = ( screenWidth - menuWidth ) / 2;
- } else {
-
- //otherwise insure a >= 30px offset from the left
- newleft = self.button.offset().left + self.button.outerWidth() / 2 - menuWidth / 2;
-
- // 30px tolerance off the edges
- if ( newleft < 30 ) {
- newleft = 30;
- } else if ( (newleft + menuWidth) > screenWidth ) {
- newleft = screenWidth - menuWidth - 30;
- }
- }
-
- self.listbox.append( self.list )
- .removeClass( "ui-selectmenu-hidden" )
- .css({
- top: newtop,
- left: newleft
- })
- .addClass( "in" );
-
- focusMenuItem();
-
- // duplicate with value set in page show for dialog sized selects
- self.isOpen = true;
- }
- },
-
- _buildList: function() {
- var self = this,
- o = this.options,
- placeholder = this.placeholder,
- optgroups = [],
- lis = [],
- dataIcon = self.isMultiple ? "checkbox-off" : "false";
-
- self.list.empty().filter( ".ui-listview" ).listview( "destroy" );
-
- // Populate menu with options from select element
- self.select.find( "option" ).each( function( i ) {
- var $this = $( this ),
- $parent = $this.parent(),
- text = $this.text(),
- anchor = "<a href='#'>"+ text +"</a>",
- classes = [],
- extraAttrs = [];
-
- // Are we inside an optgroup?
- if ( $parent.is( "optgroup" ) ) {
- var optLabel = $parent.attr( "label" );
-
- // has this optgroup already been built yet?
- if ( $.inArray( optLabel, optgroups ) === -1 ) {
- lis.push( "<li data-" + $.mobile.ns + "role='list-divider'>"+ optLabel +"</li>" );
- optgroups.push( optLabel );
- }
- }
-
- // Find placeholder text
- // TODO: Are you sure you want to use getAttribute? ^RW
- if ( !this.getAttribute( "value" ) || text.length == 0 || $this.jqmData( "placeholder" ) ) {
- if ( o.hidePlaceholderMenuItems ) {
- classes.push( "ui-selectmenu-placeholder" );
- }
- placeholder = self.placeholder = text;
- }
-
- // support disabled option tags
- if ( this.disabled ) {
- classes.push( "ui-disabled" );
- extraAttrs.push( "aria-disabled='true'" );
- }
-
- lis.push( "<li data-" + $.mobile.ns + "option-index='" + i + "' data-" + $.mobile.ns + "icon='"+ dataIcon +"' class='"+ classes.join(" ") + "' " + extraAttrs.join(" ") +">"+ anchor +"</li>" );
- });
-
- self.list.html( lis.join(" ") );
-
- self.list.find( "li" )
- .attr({ "role": "option", "tabindex": "-1" })
- .first().attr( "tabindex", "0" );
-
- // Hide header close link for single selects
- if ( !this.isMultiple ) {
- this.headerClose.hide();
- }
-
- // Hide header if it's not a multiselect and there's no placeholder
- if ( !this.isMultiple && !placeholder.length ) {
- this.header.hide();
- } else {
- this.headerTitle.text( this.placeholder );
- }
-
- // Now populated, create listview
- self.list.listview();
- },
-
- _button: function(){
- return $( "<a>", {
- "href": "#",
- "role": "button",
- // TODO value is undefined at creation
- "id": this.buttonId,
- "aria-haspopup": "true",
-
- // TODO value is undefined at creation
- "aria-owns": this.menuId
- });
- }
- });
- };
-
- $( "select" ).live( "selectmenubeforecreate", function(){
- var selectmenuWidget = $( this ).data( "selectmenu" );
-
- if( !selectmenuWidget.options.nativeMenu ){
- extendSelect( selectmenuWidget );
- }
- });
-})( jQuery );
-/*
-* jQuery Mobile Framework : "selectmenu" plugin
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-
-(function( $, undefined ) {
-
-$.widget( "mobile.selectmenu", $.mobile.widget, {
- options: {
- theme: null,
- disabled: false,
- icon: "arrow-d",
- iconpos: "right",
- inline: null,
- corners: true,
- shadow: true,
- iconshadow: true,
- menuPageTheme: "b",
- overlayTheme: "a",
- hidePlaceholderMenuItems: true,
- closeText: "Close",
- nativeMenu: true,
- initSelector: "select:not(:jqmData(role='slider'))"
- },
-
- _button: function(){
- return $( "<div/>" );
- },
-
- _theme: function(){
- var themedParent, theme;
- // if no theme is defined, try to find closest theme container
- // TODO move to core as something like findCurrentTheme
- themedParent = this.select.closest( "[class*='ui-bar-'], [class*='ui-body-']" );
- theme = themedParent.length ?
- /ui-(bar|body)-([a-z])/.exec( themedParent.attr( "class" ) )[2] :
- "c";
-
- return theme;
- },
-
- _setDisabled: function( value ) {
- this.element.attr( "disabled", value );
- this.button.attr( "aria-disabled", value );
- return this._setOption( "disabled", value );
- },
-
- _focusButton : function() {
- var self = this;
-
- setTimeout( function() {
- self.button.focus();
- }, 40);
- },
-
- // setup items that are generally necessary for select menu extension
- _preExtension: function(){
- this.select = this.element.wrap( "<div class='ui-select'>" );
- this.selectID = this.select.attr( "id" );
- this.label = $( "label[for='"+ this.selectID +"']" ).addClass( "ui-select" );
- this.isMultiple = this.select[ 0 ].multiple;
- this.options.theme = this._theme();
- this.selectOptions = this.select.find( "option" );
- },
-
- _create: function() {
- this._preExtension();
-
- // Allows for extension of the native select for custom selects and other plugins
- // see select.custom for example extension
- // TODO explore plugin registration
- this._trigger( "beforeCreate" );
-
- this.button = this._button();
-
- var self = this,
-
- options = this.options,
-
- // IE throws an exception at options.item() function when
- // there is no selected item
- // select first in this case
- selectedIndex = this.select[ 0 ].selectedIndex == -1 ? 0 : this.select[ 0 ].selectedIndex,
-
- // TODO values buttonId and menuId are undefined here
- button = this.button
- .text( $( this.select[ 0 ].options.item( selectedIndex ) ).text() )
- .insertBefore( this.select )
- .buttonMarkup( {
- theme: options.theme,
- icon: options.icon,
- iconpos: options.iconpos,
- inline: options.inline,
- corners: options.corners,
- shadow: options.shadow,
- iconshadow: options.iconshadow
- });
-
- // Opera does not properly support opacity on select elements
- // In Mini, it hides the element, but not its text
- // On the desktop,it seems to do the opposite
- // for these reasons, using the nativeMenu option results in a full native select in Opera
- if ( options.nativeMenu && window.opera && window.opera.version ) {
- this.select.addClass( "ui-select-nativeonly" );
- }
-
- // Add counter for multi selects
- if ( this.isMultiple ) {
- this.buttonCount = $( "<span>" )
- .addClass( "ui-li-count ui-btn-up-c ui-btn-corner-all" )
- .hide()
- .appendTo( button );
- }
-
- // Disable if specified
- if ( options.disabled ) {
- this.disable();
- }
-
- // Events on native select
- this.select.change( function() {
- self.refresh();
- });
-
- this.build();
- },
-
- build: function() {
- var self = this;
-
- this.select
- .appendTo( self.button )
- .bind( "vmousedown", function() {
- // Add active class to button
- self.button.addClass( $.mobile.activeBtnClass );
- })
- .bind( "focus vmouseover", function() {
- self.button.trigger( "vmouseover" );
- })
- .bind( "vmousemove", function() {
- // Remove active class on scroll/touchmove
- self.button.removeClass( $.mobile.activeBtnClass );
- })
- .bind( "change blur vmouseout", function() {
- self.button.trigger( "vmouseout" )
- .removeClass( $.mobile.activeBtnClass );
- })
- .bind( "change blur", function() {
- self.button.removeClass( "ui-btn-down-" + self.options.theme );
- });
- },
-
- selected: function() {
- return this.selectOptions.filter( ":selected" );
- },
-
- selectedIndices: function() {
- var self = this;
-
- return this.selected().map( function() {
- return self.selectOptions.index( this );
- }).get();
- },
-
- setButtonText: function() {
- var self = this, selected = this.selected();
-
- this.button.find( ".ui-btn-text" ).text( function() {
- if ( !self.isMultiple ) {
- return selected.text();
- }
-
- return selected.length ? selected.map( function() {
- return $( this ).text();
- }).get().join( ", " ) : self.placeholder;
- });
- },
-
- setButtonCount: function() {
- var selected = this.selected();
-
- // multiple count inside button
- if ( this.isMultiple ) {
- this.buttonCount[ selected.length > 1 ? "show" : "hide" ]().text( selected.length );
- }
- },
-
- refresh: function() {
- this.setButtonText();
- this.setButtonCount();
- },
-
- disable: function() {
- this._setDisabled( true );
- this.button.addClass( "ui-disabled" );
- },
-
- enable: function() {
- this._setDisabled( false );
- this.button.removeClass( "ui-disabled" );
- }
-});
-
-//auto self-init widgets
-$( document ).bind( "pagecreate create", function( e ){
- $( $.mobile.selectmenu.prototype.options.initSelector, e.target )
- .not( ":jqmData(role='none'), :jqmData(role='nojs')" )
- .selectmenu();
-});
-})( jQuery );
-/*
-* jQuery Mobile Framework : plugin for making button-like links
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-( function( $, undefined ) {
-
-$.fn.buttonMarkup = function( options ) {
- return this.each( function() {
- var el = $( this ),
- o = $.extend( {}, $.fn.buttonMarkup.defaults, el.jqmData(), options ),
-
- // Classes Defined
- innerClass = "ui-btn-inner",
- buttonClass, iconClass,
- themedParent, wrap;
-
- if ( attachEvents ) {
- attachEvents();
- }
-
- // if not, try to find closest theme container
- if ( !o.theme ) {
- themedParent = el.closest( "[class*='ui-bar-'],[class*='ui-body-']" );
- o.theme = themedParent.length ?
- /ui-(bar|body)-([a-z])/.exec( themedParent.attr( "class" ) )[2] :
- "c";
- }
-
- buttonClass = "ui-btn ui-btn-up-" + o.theme;
-
- if ( o.inline ) {
- buttonClass += " ui-btn-inline";
- }
-
- if ( o.icon ) {
- o.icon = "ui-icon-" + o.icon;
- o.iconpos = o.iconpos || "left";
-
- iconClass = "ui-icon " + o.icon;
-
- if ( o.iconshadow ) {
- iconClass += " ui-icon-shadow";
- }
- }
-
- if ( o.iconpos ) {
- buttonClass += " ui-btn-icon-" + o.iconpos;
-
- if ( o.iconpos == "notext" && !el.attr( "title" ) ) {
- el.attr( "title", el.text() );
- }
- }
-
- if ( o.corners ) {
- buttonClass += " ui-btn-corner-all";
- innerClass += " ui-btn-corner-all";
- }
-
- if ( o.shadow ) {
- buttonClass += " ui-shadow";
- }
-
- el.attr( "data-" + $.mobile.ns + "theme", o.theme )
- .addClass( buttonClass );
-
- wrap = ( "<D class='" + innerClass + "'><D class='ui-btn-text'></D>" +
- ( o.icon ? "<span class='" + iconClass + "'></span>" : "" ) +
- "</D>" ).replace( /D/g, o.wrapperEls );
-
- el.wrapInner( wrap );
- });
-};
-
-$.fn.buttonMarkup.defaults = {
- corners: true,
- shadow: true,
- iconshadow: true,
- wrapperEls: "span"
-};
-
-function closestEnabledButton( element ) {
- while ( element ) {
- var $ele = $( element );
- if ( $ele.hasClass( "ui-btn" ) && !$ele.hasClass( "ui-disabled" ) ) {
- break;
- }
- element = element.parentNode;
- }
- return element;
-}
-
-var attachEvents = function() {
- $( document ).bind( {
- "vmousedown": function( event ) {
- var btn = closestEnabledButton( event.target ),
- $btn, theme;
-
- if ( btn ) {
- $btn = $( btn );
- theme = $btn.attr( "data-" + $.mobile.ns + "theme" );
- $btn.removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-down-" + theme );
- }
- },
- "vmousecancel vmouseup": function( event ) {
- var btn = closestEnabledButton( event.target ),
- $btn, theme;
-
- if ( btn ) {
- $btn = $( btn );
- theme = $btn.attr( "data-" + $.mobile.ns + "theme" );
- $btn.removeClass( "ui-btn-down-" + theme ).addClass( "ui-btn-up-" + theme );
- }
- },
- "vmouseover focus": function( event ) {
- var btn = closestEnabledButton( event.target ),
- $btn, theme;
-
- if ( btn ) {
- $btn = $( btn );
- theme = $btn.attr( "data-" + $.mobile.ns + "theme" );
- $btn.removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-hover-" + theme );
- }
- },
- "vmouseout blur": function( event ) {
- var btn = closestEnabledButton( event.target ),
- $btn, theme;
-
- if ( btn ) {
- $btn = $( btn );
- theme = $btn.attr( "data-" + $.mobile.ns + "theme" );
- $btn.removeClass( "ui-btn-hover-" + theme ).addClass( "ui-btn-up-" + theme );
- }
- }
- });
-
- attachEvents = null;
-};
-
-//links in bars, or those with data-role become buttons
-//auto self-init widgets
-$( document ).bind( "pagecreate create", function( e ){
-
- $( ":jqmData(role='button'), .ui-bar > a, .ui-header > a, .ui-footer > a, .ui-bar > :jqmData(role='controlgroup') > a", e.target )
- .not( ".ui-btn, :jqmData(role='none'), :jqmData(role='nojs')" )
- .buttonMarkup();
-});
-
-})( jQuery );
-/*
-* jQuery Mobile Framework: "controlgroup" plugin - corner-rounding for groups of buttons, checks, radios, etc
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-(function( $, undefined ) {
-
-$.fn.controlgroup = function( options ) {
-
- return this.each(function() {
-
- var $el = $( this ),
- o = $.extend({
- direction: $el.jqmData( "type" ) || "vertical",
- shadow: false,
- excludeInvisible: true
- }, options ),
- groupheading = $el.find( ">legend" ),
- flCorners = o.direction == "horizontal" ? [ "ui-corner-left", "ui-corner-right" ] : [ "ui-corner-top", "ui-corner-bottom" ],
- type = $el.find( "input:eq(0)" ).attr( "type" );
-
- // Replace legend with more stylable replacement div
- if ( groupheading.length ) {
- $el.wrapInner( "<div class='ui-controlgroup-controls'></div>" );
- $( "<div role='heading' class='ui-controlgroup-label'>" + groupheading.html() + "</div>" ).insertBefore( $el.children(0) );
- groupheading.remove();
- }
-
- $el.addClass( "ui-corner-all ui-controlgroup ui-controlgroup-" + o.direction );
-
- // TODO: This should be moved out to the closure
- // otherwise it is redefined each time controlgroup() is called
- function flipClasses( els ) {
- els.removeClass( "ui-btn-corner-all ui-shadow" )
- .eq( 0 ).addClass( flCorners[ 0 ] )
- .end()
- .filter( ":last" ).addClass( flCorners[ 1 ] ).addClass( "ui-controlgroup-last" );
- }
-
- flipClasses( $el.find( ".ui-btn" + ( o.excludeInvisible ? ":visible" : "" ) ) );
- flipClasses( $el.find( ".ui-btn-inner" ) );
-
- if ( o.shadow ) {
- $el.addClass( "ui-shadow" );
- }
- });
-};
-
-//auto self-init widgets
-$( document ).bind( "pagecreate create", function( e ){
- $( ":jqmData(role='controlgroup')", e.target ).controlgroup({ excludeInvisible: false });
-});
-
-})(jQuery);/*
-* jQuery Mobile Framework : "fieldcontain" plugin - simple class additions to make form row separators
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-
-(function( $, undefined ) {
-
-$( document ).bind( "pagecreate create", function( e ){
-
- //links within content areas
- $( e.target )
- .find( "a" )
- .not( ".ui-btn, .ui-link-inherit, :jqmData(role='none'), :jqmData(role='nojs')" )
- .addClass( "ui-link" );
-
-});
-
-})( jQuery );/*
-* jQuery Mobile Framework : "fixHeaderFooter" plugin - on-demand positioning for headers,footers
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-
-(function( $, undefined ) {
-
-var slideDownClass = "ui-header-fixed ui-fixed-inline fade",
- slideUpClass = "ui-footer-fixed ui-fixed-inline fade",
-
- slideDownSelector = ".ui-header:jqmData(position='fixed')",
- slideUpSelector = ".ui-footer:jqmData(position='fixed')";
-
-$.fn.fixHeaderFooter = function( options ) {
-
- if ( !$.support.scrollTop ) {
- return this;
- }
-
- return this.each(function() {
- var $this = $( this );
-
- if ( $this.jqmData( "fullscreen" ) ) {
- $this.addClass( "ui-page-fullscreen" );
- }
-
- // Should be slidedown
- $this.find( slideDownSelector ).addClass( slideDownClass );
-
- // Should be slideup
- $this.find( slideUpSelector ).addClass( slideUpClass );
- });
-};
-
-// single controller for all showing,hiding,toggling
-$.mobile.fixedToolbars = (function() {
-
- if ( !$.support.scrollTop || $.support.touchOverflow ) {
- return;
- }
-
- var stickyFooter, delayTimer,
- currentstate = "inline",
- autoHideMode = false,
- showDelay = 100,
- ignoreTargets = "a,input,textarea,select,button,label,.ui-header-fixed,.ui-footer-fixed",
- toolbarSelector = ".ui-header-fixed:first, .ui-footer-fixed:not(.ui-footer-duplicate):last",
- // for storing quick references to duplicate footers
- supportTouch = $.support.touch,
- touchStartEvent = supportTouch ? "touchstart" : "mousedown",
- touchStopEvent = supportTouch ? "touchend" : "mouseup",
- stateBefore = null,
- scrollTriggered = false,
- touchToggleEnabled = true;
-
- function showEventCallback( event ) {
- // An event that affects the dimensions of the visual viewport has
- // been triggered. If the header and/or footer for the current page are in overlay
- // mode, we want to hide them, and then fire off a timer to show them at a later
- // point. Events like a resize can be triggered continuously during a scroll, on
- // some platforms, so the timer is used to delay the actual positioning until the
- // flood of events have subsided.
- //
- // If we are in autoHideMode, we don't do anything because we know the scroll
- // callbacks for the plugin will fire off a show when the scrolling has stopped.
- if ( !autoHideMode && currentstate === "overlay" ) {
- if ( !delayTimer ) {
- $.mobile.fixedToolbars.hide( true );
- }
-
- $.mobile.fixedToolbars.startShowTimer();
- }
- }
-
- $(function() {
- var $document = $( document ),
- $window = $( window );
-
- $document
- .bind( "vmousedown", function( event ) {
- if ( touchToggleEnabled ) {
- stateBefore = currentstate;
- }
- })
- .bind( "vclick", function( event ) {
- if ( touchToggleEnabled ) {
-
- if ( $(event.target).closest( ignoreTargets ).length ) {
- return;
- }
-
- if ( !scrollTriggered ) {
- $.mobile.fixedToolbars.toggle( stateBefore );
- stateBefore = null;
- }
- }
- })
- .bind( "silentscroll", showEventCallback );
-
-
- // The below checks first for a $(document).scrollTop() value, and if zero, binds scroll events to $(window) instead.
- // If the scrollTop value is actually zero, both will return zero anyway.
- //
- // Works with $(document), not $(window) : Opera Mobile (WinMO phone; kinda broken anyway)
- // Works with $(window), not $(document) : IE 7/8
- // Works with either $(window) or $(document) : Chrome, FF 3.6/4, Android 1.6/2.1, iOS
- // Needs work either way : BB5, Opera Mobile (iOS)
-
- ( ( $document.scrollTop() === 0 ) ? $window : $document )
- .bind( "scrollstart", function( event ) {
-
- scrollTriggered = true;
-
- if ( stateBefore === null ) {
- stateBefore = currentstate;
- }
-
- // We only enter autoHideMode if the headers/footers are in
- // an overlay state or the show timer was started. If the
- // show timer is set, clear it so the headers/footers don't
- // show up until after we're done scrolling.
- var isOverlayState = stateBefore == "overlay";
-
- autoHideMode = isOverlayState || !!delayTimer;
-
- if ( autoHideMode ) {
- $.mobile.fixedToolbars.clearShowTimer();
-
- if ( isOverlayState ) {
- $.mobile.fixedToolbars.hide( true );
- }
- }
- })
- .bind( "scrollstop", function( event ) {
-
- if ( $( event.target ).closest( ignoreTargets ).length ) {
- return;
- }
-
- scrollTriggered = false;
-
- if ( autoHideMode ) {
- $.mobile.fixedToolbars.startShowTimer();
- autoHideMode = false;
- }
- stateBefore = null;
- });
-
- $window.bind( "resize", showEventCallback );
- });
-
- // 1. Before page is shown, check for duplicate footer
- // 2. After page is shown, append footer to new page
- $( ".ui-page" )
- .live( "pagebeforeshow", function( event, ui ) {
-
- var page = $( event.target ),
- footer = page.find( ":jqmData(role='footer')" ),
- id = footer.data( "id" ),
- prevPage = ui.prevPage,
- prevFooter = prevPage && prevPage.find( ":jqmData(role='footer')" ),
- prevFooterMatches = prevFooter.length && prevFooter.jqmData( "id" ) === id;
-
- if ( id && prevFooterMatches ) {
- stickyFooter = footer;
- setTop( stickyFooter.removeClass( "fade in out" ).appendTo( $.mobile.pageContainer ) );
- }
- })
- .live( "pageshow", function( event, ui ) {
-
- var $this = $( this );
-
- if ( stickyFooter && stickyFooter.length ) {
-
- setTimeout(function() {
- setTop( stickyFooter.appendTo( $this ).addClass( "fade" ) );
- stickyFooter = null;
- }, 500);
- }
-
- $.mobile.fixedToolbars.show( true, this );
- });
-
- // When a collapsiable is hidden or shown we need to trigger the fixed toolbar to reposition itself (#1635)
- $( ".ui-collapsible-contain" ).live( "collapse expand", showEventCallback );
-
- // element.getBoundingClientRect() is broken in iOS 3.2.1 on the iPad. The
- // coordinates inside of the rect it returns don't have the page scroll position
- // factored out of it like the other platforms do. To get around this,
- // we'll just calculate the top offset the old fashioned way until core has
- // a chance to figure out how to handle this situation.
- //
- // TODO: We'll need to get rid of getOffsetTop() once a fix gets folded into core.
-
- function getOffsetTop( ele ) {
- var top = 0,
- op, body;
-
- if ( ele ) {
- body = document.body;
- op = ele.offsetParent;
- top = ele.offsetTop;
-
- while ( ele && ele != body ) {
- top += ele.scrollTop || 0;
-
- if ( ele == op ) {
- top += op.offsetTop;
- op = ele.offsetParent;
- }
-
- ele = ele.parentNode;
- }
- }
- return top;
- }
-
- function setTop( el ) {
- var fromTop = $(window).scrollTop(),
- thisTop = getOffsetTop( el[ 0 ] ), // el.offset().top returns the wrong value on iPad iOS 3.2.1, call our workaround instead.
- thisCSStop = el.css( "top" ) == "auto" ? 0 : parseFloat(el.css( "top" )),
- screenHeight = window.innerHeight,
- thisHeight = el.outerHeight(),
- useRelative = el.parents( ".ui-page:not(.ui-page-fullscreen)" ).length,
- relval;
-
- if ( el.is( ".ui-header-fixed" ) ) {
-
- relval = fromTop - thisTop + thisCSStop;
-
- if ( relval < thisTop ) {
- relval = 0;
- }
-
- return el.css( "top", useRelative ? relval : fromTop );
- } else {
- // relval = -1 * (thisTop - (fromTop + screenHeight) + thisCSStop + thisHeight);
- // if ( relval > thisTop ) { relval = 0; }
- relval = fromTop + screenHeight - thisHeight - (thisTop - thisCSStop );
-
- return el.css( "top", useRelative ? relval : fromTop + screenHeight - thisHeight );
- }
- }
-
- // Exposed methods
- return {
-
- show: function( immediately, page ) {
-
- $.mobile.fixedToolbars.clearShowTimer();
-
- currentstate = "overlay";
-
- var $ap = page ? $( page ) :
- ( $.mobile.activePage ? $.mobile.activePage :
- $( ".ui-page-active" ) );
-
- return $ap.children( toolbarSelector ).each(function() {
-
- var el = $( this ),
- fromTop = $( window ).scrollTop(),
- // el.offset().top returns the wrong value on iPad iOS 3.2.1, call our workaround instead.
- thisTop = getOffsetTop( el[ 0 ] ),
- screenHeight = window.innerHeight,
- thisHeight = el.outerHeight(),
- alreadyVisible = ( el.is( ".ui-header-fixed" ) && fromTop <= thisTop + thisHeight ) ||
- ( el.is( ".ui-footer-fixed" ) && thisTop <= fromTop + screenHeight );
-
- // Add state class
- el.addClass( "ui-fixed-overlay" ).removeClass( "ui-fixed-inline" );
-
- if ( !alreadyVisible && !immediately ) {
- el.animationComplete(function() {
- el.removeClass( "in" );
- }).addClass( "in" );
- }
- setTop(el);
- });
- },
-
- hide: function( immediately ) {
-
- currentstate = "inline";
-
- var $ap = $.mobile.activePage ? $.mobile.activePage :
- $( ".ui-page-active" );
-
- return $ap.children( toolbarSelector ).each(function() {
-
- var el = $(this),
- thisCSStop = el.css( "top" ),
- classes;
-
- thisCSStop = thisCSStop == "auto" ? 0 :
- parseFloat(thisCSStop);
-
- // Add state class
- el.addClass( "ui-fixed-inline" ).removeClass( "ui-fixed-overlay" );
-
- if ( thisCSStop < 0 || ( el.is( ".ui-header-fixed" ) && thisCSStop !== 0 ) ) {
-
- if ( immediately ) {
- el.css( "top", 0);
- } else {
-
- if ( el.css( "top" ) !== "auto" && parseFloat( el.css( "top" ) ) !== 0 ) {
-
- classes = "out reverse";
-
- el.animationComplete(function() {
- el.removeClass( classes ).css( "top", 0 );
- }).addClass( classes );
- }
- }
- }
- });
- },
-
- startShowTimer: function() {
-
- $.mobile.fixedToolbars.clearShowTimer();
-
- var args = [].slice.call( arguments );
-
- delayTimer = setTimeout(function() {
- delayTimer = undefined;
- $.mobile.fixedToolbars.show.apply( null, args );
- }, showDelay);
- },
-
- clearShowTimer: function() {
- if ( delayTimer ) {
- clearTimeout( delayTimer );
- }
- delayTimer = undefined;
- },
-
- toggle: function( from ) {
- if ( from ) {
- currentstate = from;
- }
- return ( currentstate === "overlay" ) ? $.mobile.fixedToolbars.hide() :
- $.mobile.fixedToolbars.show();
- },
-
- setTouchToggleEnabled: function( enabled ) {
- touchToggleEnabled = enabled;
- }
- };
-})();
-
-// TODO - Deprecated namepace on $. Remove in a later release
-$.fixedToolbars = $.mobile.fixedToolbars;
-
-//auto self-init widgets
-$( document ).bind( "pagecreate create", function( event ) {
-
- if ( $( ":jqmData(position='fixed')", event.target ).length ) {
-
- $( event.target ).each(function() {
-
- if ( !$.support.scrollTop || $.support.touchOverflow ) {
- return this;
- }
-
- var $this = $( this );
-
- if ( $this.jqmData( "fullscreen" ) ) {
- $this.addClass( "ui-page-fullscreen" );
- }
-
- // Should be slidedown
- $this.find( slideDownSelector ).addClass( slideDownClass );
-
- // Should be slideup
- $this.find( slideUpSelector ).addClass( slideUpClass );
-
- })
-
- }
-});
-
-})( jQuery );
-/*
-* jQuery Mobile Framework : "fixHeaderFooter" native plugin - Behavior for "fixed" headers,footers, and scrolling inner content
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-
-(function( $, undefined ) {
-
-$.mobile.touchOverflowEnabled = false;
-
-$( document ).bind( "pagecreate", function( event ) {
- if( $.support.touchOverflow && $.mobile.touchOverflowEnabled ){
-
- var $target = $( event.target ),
- scrollStartY = 0;
-
- if( $target.is( ":jqmData(role='page')" ) ){
-
- $target.each(function() {
- var $page = $( this ),
- $fixies = $page.find( ":jqmData(role='header'), :jqmData(role='footer')" ).filter( ":jqmData(position='fixed')" ),
- fullScreen = $page.jqmData( "fullscreen" ),
- $scrollElem = $fixies.length ? $page.find( ".ui-content" ) : $page;
-
- $page.addClass( "ui-mobile-touch-overflow" );
-
- $scrollElem.bind( "scrollstop", function(){
- if( $scrollElem.scrollTop() > 0 ){
- window.scrollTo( 0, $.mobile.defaultHomeScroll );
- }
- });
-
- if( $fixies.length ){
-
- $page.addClass( "ui-native-fixed" );
-
- if( fullScreen ){
-
- $page.addClass( "ui-native-fullscreen" );
-
- $fixies.addClass( "fade in" );
-
- $( document ).bind( "vclick", function(){
- $fixies
- .removeClass( "ui-native-bars-hidden" )
- .toggleClass( "in out" )
- .animationComplete(function(){
- $(this).not( ".in" ).addClass( "ui-native-bars-hidden" );
- });
- });
- }
- }
- });
- }
- }
-});
-
-})( jQuery );
-/*
-* jQuery Mobile Framework : resolution and CSS media query related helpers and behavior
-* Copyright (c) jQuery Project
-* Dual licensed under the MIT or GPL Version 2 licenses.
-* http://jquery.org/license
-*/
-(function( $, undefined ) {
-
-var $window = $( window ),
- $html = $( "html" ),
-
- //media-query-like width breakpoints, which are translated to classes on the html element
- resolutionBreakpoints = [ 320, 480, 768, 1024 ];
-
-/*
- private function for adding/removing breakpoint classes to HTML element for faux media-query support
- It does not require media query support, instead using JS to detect screen width > cross-browser support
- This function is called on orientationchange, resize, and mobileinit, and is bound via the 'htmlclass' event namespace
-*/
-function detectResolutionBreakpoints() {
- var currWidth = $window.width(),
- minPrefix = "min-width-",
- maxPrefix = "max-width-",
- minBreakpoints = [],
- maxBreakpoints = [],
- unit = "px",
- breakpointClasses;
-
- $html.removeClass( minPrefix + resolutionBreakpoints.join(unit + " " + minPrefix) + unit + " " +
- maxPrefix + resolutionBreakpoints.join( unit + " " + maxPrefix) + unit );
-
- $.each( resolutionBreakpoints, function( i, breakPoint ) {
- if( currWidth >= breakPoint ) {
- minBreakpoints.push( minPrefix + breakPoint + unit );
- }
- if( currWidth <= breakPoint ) {
- maxBreakpoints.push( maxPrefix + breakPoint + unit );
- }
- });
-
- if ( minBreakpoints.length ) {
- breakpointClasses = minBreakpoints.join(" ");
- }
- if ( maxBreakpoints.length ) {
- breakpointClasses += " " + maxBreakpoints.join(" ");
- }
-
- $html.addClass( breakpointClasses );
-};
-
-/* $.mobile.addResolutionBreakpoints method:
- pass either a number or an array of numbers and they'll be added to the min/max breakpoint classes
- Examples:
- $.mobile.addResolutionBreakpoints( 500 );
- $.mobile.addResolutionBreakpoints( [500, 1200] );
-*/
-$.mobile.addResolutionBreakpoints = function( newbps ) {
- if( $.type( newbps ) === "array" ){
- resolutionBreakpoints = resolutionBreakpoints.concat( newbps );
- } else {
- resolutionBreakpoints.push( newbps );
- }
-
- resolutionBreakpoints.sort(function( a, b ) {
- return a - b;
- });
-
- detectResolutionBreakpoints();
-};
-
-/* on mobileinit, add classes to HTML element
- and set handlers to update those on orientationchange and resize
-*/
-$( document ).bind( "mobileinit.htmlclass", function() {
- // bind to orientationchange and resize
- // to add classes to HTML element for min/max breakpoints and orientation
-
- var ev = $.support.orientation;
-
- $window.bind( "orientationchange.htmlclass throttledresize.htmlclass", function( event ) {
-
- // add orientation class to HTML element on flip/resize.
- if ( event.orientation ) {
- $html.removeClass( "portrait landscape" ).addClass( event.orientation );
- }
-
- // add classes to HTML element for min/max breakpoints
- detectResolutionBreakpoints();
- });
-});
-
-/* Manually trigger an orientationchange event when the dom ready event fires.
- This will ensure that any viewport meta tag that may have been injected
- has taken effect already, allowing us to properly calculate the width of the
- document.
-*/
-$(function() {
- //trigger event manually
- $window.trigger( "orientationchange.htmlclass" );
-});
-
-})(jQuery);/*!
- * jQuery Mobile v@VERSION
- * http://jquerymobile.com/
- *
- * Copyright 2010, jQuery Project
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- */
-
-(function( $, window, undefined ) {
- var $html = $( "html" ),
- $head = $( "head" ),
- $window = $( window );
-
- // trigger mobileinit event - useful hook for configuring $.mobile settings before they're used
- $( window.document ).trigger( "mobileinit" );
-
- // support conditions
- // if device support condition(s) aren't met, leave things as they are -> a basic, usable experience,
- // otherwise, proceed with the enhancements
- if ( !$.mobile.gradeA() ) {
- return;
- }
-
- // override ajaxEnabled on platforms that have known conflicts with hash history updates
- // or generally work better browsing in regular http for full page refreshes (BB5, Opera Mini)
- if ( $.mobile.ajaxBlacklist ) {
- $.mobile.ajaxEnabled = false;
- }
-
- // add mobile, initial load "rendering" classes to docEl
- $html.addClass( "ui-mobile ui-mobile-rendering" );
-
- // loading div which appears during Ajax requests
- // will not appear if $.mobile.loadingMessage is false
- var $loader = $( "<div class='ui-loader ui-body-a ui-corner-all'><span class='ui-icon ui-icon-loading spin'></span><h1></h1></div>" );
-
- $.extend($.mobile, {
- // turn on/off page loading message.
- showPageLoadingMsg: function() {
- if ( $.mobile.loadingMessage ) {
- var activeBtn = $( "." + $.mobile.activeBtnClass ).first();
-
- $loader
- .find( "h1" )
- .text( $.mobile.loadingMessage )
- .end()
- .appendTo( $.mobile.pageContainer )
- // position at y center (if scrollTop supported), above the activeBtn (if defined), or just 100px from top
- .css({
- top: $.support.scrollTop && $window.scrollTop() + $window.height() / 2 ||
- activeBtn.length && activeBtn.offset().top || 100
- });
- }
-
- $html.addClass( "ui-loading" );
- },
-
- hidePageLoadingMsg: function() {
- $html.removeClass( "ui-loading" );
- },
-
- // XXX: deprecate for 1.0
- pageLoading: function ( done ) {
- if ( done ) {
- $.mobile.hidePageLoadingMsg();
- } else {
- $.mobile.showPageLoadingMsg();
- }
- },
-
- // find and enhance the pages in the dom and transition to the first page.
- initializePage: function() {
- // find present pages
- var $pages = $( ":jqmData(role='page')" );
-
- // if no pages are found, create one with body's inner html
- if ( !$pages.length ) {
- $pages = $( "body" ).wrapInner( "<div data-" + $.mobile.ns + "role='page'></div>" ).children( 0 );
- }
-
- // add dialogs, set data-url attrs
- $pages.add( ":jqmData(role='dialog')" ).each(function() {
- var $this = $(this);
-
- // unless the data url is already set set it to the pathname
- if ( !$this.jqmData("url") ) {
- $this.attr( "data-" + $.mobile.ns + "url", $this.attr( "id" ) || location.pathname + location.search );
- }
- });
-
- // define first page in dom case one backs out to the directory root (not always the first page visited, but defined as fallback)
- $.mobile.firstPage = $pages.first();
-
- // define page container
- $.mobile.pageContainer = $pages.first().parent().addClass( "ui-mobile-viewport" );
-
- // cue page loading message
- $.mobile.showPageLoadingMsg();
-
- // if hashchange listening is disabled or there's no hash deeplink, change to the first page in the DOM
- if ( !$.mobile.hashListeningEnabled || !$.mobile.path.stripHash( location.hash ) ) {
- $.mobile.changePage( $.mobile.firstPage, { transition: "none", reverse: true, changeHash: false, fromHashChange: true } );
- }
- // otherwise, trigger a hashchange to load a deeplink
- else {
- $window.trigger( "hashchange", [ true ] );
- }
- }
- });
-
- // initialize events now, after mobileinit has occurred
- $.mobile._registerInternalEvents();
-
- // check which scrollTop value should be used by scrolling to 1 immediately at domready
- // then check what the scroll top is. Android will report 0... others 1
- // note that this initial scroll won't hide the address bar. It's just for the check.
- $(function() {
- window.scrollTo( 0, 1 );
-
- // if defaultHomeScroll hasn't been set yet, see if scrollTop is 1
- // it should be 1 in most browsers, but android treats 1 as 0 (for hiding addr bar)
- // so if it's 1, use 0 from now on
- $.mobile.defaultHomeScroll = ( !$.support.scrollTop || $(window).scrollTop() === 1 ) ? 0 : 1;
-
- //dom-ready inits
- if( $.mobile.autoInitializePage ){
- $.mobile.initializePage();
- }
-
- // window load event
- // hide iOS browser chrome on load
- $window.load( $.mobile.silentScroll );
- });
-})( jQuery, this );
-
--- /dev/null
+++ b/js/jquery.mobile-1.0rc1.js
@@ -1,1 +1,6765 @@
-
+/*!
+ * jQuery Mobile v1.0rc1
+ * http://jquerymobile.com/
+ *
+ * Copyright 2010, jQuery Project
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ */
+/*!
+ * jQuery UI Widget @VERSION
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Widget
+ */
+(function( $, undefined ) {
+
+// jQuery 1.4+
+if ( $.cleanData ) {
+ var _cleanData = $.cleanData;
+ $.cleanData = function( elems ) {
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ $( elem ).triggerHandler( "remove" );
+ }
+ _cleanData( elems );
+ };
+} else {
+ var _remove = $.fn.remove;
+ $.fn.remove = function( selector, keepData ) {
+ return this.each(function() {
+ if ( !keepData ) {
+ if ( !selector || $.filter( selector, [ this ] ).length ) {
+ $( "*", this ).add( [ this ] ).each(function() {
+ $( this ).triggerHandler( "remove" );
+ });
+ }
+ }
+ return _remove.call( $(this), selector, keepData );
+ });
+ };
+}
+
+$.widget = function( name, base, prototype ) {
+ var namespace = name.split( "." )[ 0 ],
+ fullName;
+ name = name.split( "." )[ 1 ];
+ fullName = namespace + "-" + name;
+
+ if ( !prototype ) {
+ prototype = base;
+ base = $.Widget;
+ }
+
+ // create selector for plugin
+ $.expr[ ":" ][ fullName ] = function( elem ) {
+ return !!$.data( elem, name );
+ };
+
+ $[ namespace ] = $[ namespace ] || {};
+ $[ namespace ][ name ] = function( options, element ) {
+ // allow instantiation without initializing for simple inheritance
+ if ( arguments.length ) {
+ this._createWidget( options, element );
+ }
+ };
+
+ var basePrototype = new base();
+ // we need to make the options hash a property directly on the new instance
+ // otherwise we'll modify the options hash on the prototype that we're
+ // inheriting from
+// $.each( basePrototype, function( key, val ) {
+// if ( $.isPlainObject(val) ) {
+// basePrototype[ key ] = $.extend( {}, val );
+// }
+// });
+ basePrototype.options = $.extend( true, {}, basePrototype.options );
+ $[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
+ namespace: namespace,
+ widgetName: name,
+ widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
+ widgetBaseClass: fullName
+ }, prototype );
+
+ $.widget.bridge( name, $[ namespace ][ name ] );
+};
+
+$.widget.bridge = function( name, object ) {
+ $.fn[ name ] = function( options ) {
+ var isMethodCall = typeof options === "string",
+ args = Array.prototype.slice.call( arguments, 1 ),
+ returnValue = this;
+
+ // allow multiple hashes to be passed on init
+ options = !isMethodCall && args.length ?
+ $.extend.apply( null, [ true, options ].concat(args) ) :
+ options;
+
+ // prevent calls to internal methods
+ if ( isMethodCall && options.charAt( 0 ) === "_" ) {
+ return returnValue;
+ }
+
+ if ( isMethodCall ) {
+ this.each(function() {
+ var instance = $.data( this, name );
+ if ( !instance ) {
+ throw "cannot call methods on " + name + " prior to initialization; " +
+ "attempted to call method '" + options + "'";
+ }
+ if ( !$.isFunction( instance[options] ) ) {
+ throw "no such method '" + options + "' for " + name + " widget instance";
+ }
+ var methodValue = instance[ options ].apply( instance, args );
+ if ( methodValue !== instance && methodValue !== undefined ) {
+ returnValue = methodValue;
+ return false;
+ }
+ });
+ } else {
+ this.each(function() {
+ var instance = $.data( this, name );
+ if ( instance ) {
+ instance.option( options || {} )._init();
+ } else {
+ $.data( this, name, new object( options, this ) );
+ }
+ });
+ }
+
+ return returnValue;
+ };
+};
+
+$.Widget = function( options, element ) {
+ // allow instantiation without initializing for simple inheritance
+ if ( arguments.length ) {
+ this._createWidget( options, element );
+ }
+};
+
+$.Widget.prototype = {
+ widgetName: "widget",
+ widgetEventPrefix: "",
+ options: {
+ disabled: false
+ },
+ _createWidget: function( options, element ) {
+ // $.widget.bridge stores the plugin instance, but we do it anyway
+ // so that it's stored even before the _create function runs
+ $.data( element, this.widgetName, this );
+ this.element = $( element );
+ this.options = $.extend( true, {},
+ this.options,
+ this._getCreateOptions(),
+ options );
+
+ var self = this;
+ this.element.bind( "remove." + this.widgetName, function() {
+ self.destroy();
+ });
+
+ this._create();
+ this._trigger( "create" );
+ this._init();
+ },
+ _getCreateOptions: function() {
+ var options = {};
+ if ( $.metadata ) {
+ options = $.metadata.get( element )[ this.widgetName ];
+ }
+ return options;
+ },
+ _create: function() {},
+ _init: function() {},
+
+ destroy: function() {
+ this.element
+ .unbind( "." + this.widgetName )
+ .removeData( this.widgetName );
+ this.widget()
+ .unbind( "." + this.widgetName )
+ .removeAttr( "aria-disabled" )
+ .removeClass(
+ this.widgetBaseClass + "-disabled " +
+ "ui-state-disabled" );
+ },
+
+ widget: function() {
+ return this.element;
+ },
+
+ option: function( key, value ) {
+ var options = key;
+
+ if ( arguments.length === 0 ) {
+ // don't return a reference to the internal hash
+ return $.extend( {}, this.options );
+ }
+
+ if (typeof key === "string" ) {
+ if ( value === undefined ) {
+ return this.options[ key ];
+ }
+ options = {};
+ options[ key ] = value;
+ }
+
+ this._setOptions( options );
+
+ return this;
+ },
+ _setOptions: function( options ) {
+ var self = this;
+ $.each( options, function( key, value ) {
+ self._setOption( key, value );
+ });
+
+ return this;
+ },
+ _setOption: function( key, value ) {
+ this.options[ key ] = value;
+
+ if ( key === "disabled" ) {
+ this.widget()
+ [ value ? "addClass" : "removeClass"](
+ this.widgetBaseClass + "-disabled" + " " +
+ "ui-state-disabled" )
+ .attr( "aria-disabled", value );
+ }
+
+ return this;
+ },
+
+ enable: function() {
+ return this._setOption( "disabled", false );
+ },
+ disable: function() {
+ return this._setOption( "disabled", true );
+ },
+
+ _trigger: function( type, event, data ) {
+ var callback = this.options[ type ];
+
+ event = $.Event( event );
+ event.type = ( type === this.widgetEventPrefix ?
+ type :
+ this.widgetEventPrefix + type ).toLowerCase();
+ data = data || {};
+
+ // copy original event properties over to the new event
+ // this would happen if we could call $.event.fix instead of $.Event
+ // but we don't have a way to force an event to be fixed multiple times
+ if ( event.originalEvent ) {
+ for ( var i = $.event.props.length, prop; i; ) {
+ prop = $.event.props[ --i ];
+ event[ prop ] = event.originalEvent[ prop ];
+ }
+ }
+
+ this.element.trigger( event, data );
+
+ return !( $.isFunction(callback) &&
+ callback.call( this.element[0], event, data ) === false ||
+ event.isDefaultPrevented() );
+ }
+};
+
+})( jQuery );
+/*
+* jQuery Mobile Framework : widget factory extentions for mobile
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+
+(function( $, undefined ) {
+
+$.widget( "mobile.widget", {
+ // decorate the parent _createWidget to trigger `widgetinit` for users
+ // who wish to do post post `widgetcreate` alterations/additions
+ //
+ // TODO create a pull request for jquery ui to trigger this event
+ // in the original _createWidget
+ _createWidget: function() {
+ $.Widget.prototype._createWidget.apply( this, arguments );
+ this._trigger( 'init' );
+ },
+
+ _getCreateOptions: function() {
+
+ var elem = this.element,
+ options = {};
+
+ $.each( this.options, function( option ) {
+
+ var value = elem.jqmData( option.replace( /[A-Z]/g, function( c ) {
+ return "-" + c.toLowerCase();
+ })
+ );
+
+ if ( value !== undefined ) {
+ options[ option ] = value;
+ }
+ });
+
+ return options;
+ }
+});
+
+})( jQuery );
+/*
+* jQuery Mobile Framework : a workaround for window.matchMedia
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+(function( $, undefined ) {
+
+var $window = $( window ),
+ $html = $( "html" );
+
+/* $.mobile.media method: pass a CSS media type or query and get a bool return
+ note: this feature relies on actual media query support for media queries, though types will work most anywhere
+ examples:
+ $.mobile.media('screen') //>> tests for screen media type
+ $.mobile.media('screen and (min-width: 480px)') //>> tests for screen media type with window width > 480px
+ $.mobile.media('@media screen and (-webkit-min-device-pixel-ratio: 2)') //>> tests for webkit 2x pixel ratio (iPhone 4)
+*/
+$.mobile.media = (function() {
+ // TODO: use window.matchMedia once at least one UA implements it
+ var cache = {},
+ testDiv = $( "<div id='jquery-mediatest'>" ),
+ fakeBody = $( "<body>" ).append( testDiv );
+
+ return function( query ) {
+ if ( !( query in cache ) ) {
+ var styleBlock = document.createElement( "style" ),
+ cssrule = "@media " + query + " { #jquery-mediatest { position:absolute; } }";
+
+ //must set type for IE!
+ styleBlock.type = "text/css";
+
+ if ( styleBlock.styleSheet ){
+ styleBlock.styleSheet.cssText = cssrule;
+ } else {
+ styleBlock.appendChild( document.createTextNode(cssrule) );
+ }
+
+ $html.prepend( fakeBody ).prepend( styleBlock );
+ cache[ query ] = testDiv.css( "position" ) === "absolute";
+ fakeBody.add( styleBlock ).remove();
+ }
+ return cache[ query ];
+ };
+})();
+
+})(jQuery);/*
+* jQuery Mobile Framework : support tests
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+*/
+
+(function( $, undefined ) {
+
+var fakeBody = $( "<body>" ).prependTo( "html" ),
+ fbCSS = fakeBody[ 0 ].style,
+ vendors = [ "Webkit", "Moz", "O" ],
+ webos = "palmGetResource" in window, //only used to rule out scrollTop
+ bb = window.blackberry; //only used to rule out box shadow, as it's filled opaque on BB
+
+// thx Modernizr
+function propExists( prop ) {
+ var uc_prop = prop.charAt( 0 ).toUpperCase() + prop.substr( 1 ),
+ props = ( prop + " " + vendors.join( uc_prop + " " ) + uc_prop ).split( " " );
+
+ for ( var v in props ){
+ if ( fbCSS[ props[ v ] ] !== undefined ) {
+ return true;
+ }
+ }
+}
+
+// Test for dynamic-updating base tag support ( allows us to avoid href,src attr rewriting )
+function baseTagTest() {
+ var fauxBase = location.protocol + "//" + location.host + location.pathname + "ui-dir/",
+ base = $( "head base" ),
+ fauxEle = null,
+ href = "",
+ link, rebase;
+
+ if ( !base.length ) {
+ base = fauxEle = $( "<base>", { "href": fauxBase }).appendTo( "head" );
+ } else {
+ href = base.attr( "href" );
+ }
+
+ link = $( "<a href='testurl'></a>" ).prependTo( fakeBody );
+ rebase = link[ 0 ].href;
+ base[ 0 ].href = href ? href : location.pathname;
+
+ if ( fauxEle ) {
+ fauxEle.remove();
+ }
+ return rebase.indexOf( fauxBase ) === 0;
+}
+
+
+// non-UA-based IE version check by James Padolsey, modified by jdalton - from http://gist.github.com/527683
+// allows for inclusion of IE 6+, including Windows Mobile 7
+$.mobile.browser = {};
+$.mobile.browser.ie = (function() {
+ var v = 3,
+ div = document.createElement( "div" ),
+ a = div.all || [];
+
+ while ( div.innerHTML = "<!--[if gt IE " + ( ++v ) + "]><br><![endif]-->", a[ 0 ] );
+
+ return v > 4 ? v : !v;
+})();
+
+
+$.extend( $.support, {
+ orientation: "orientation" in window,
+ touch: "ontouchend" in document,
+ cssTransitions: "WebKitTransitionEvent" in window,
+ pushState: "pushState" in history && "replaceState" in history,
+ mediaquery: $.mobile.media( "only all" ),
+ cssPseudoElement: !!propExists( "content" ),
+ touchOverflow: !!propExists( "overflowScrolling" ),
+ boxShadow: !!propExists( "boxShadow" ) && !bb,
+ scrollTop: ( "pageXOffset" in window || "scrollTop" in document.documentElement || "scrollTop" in fakeBody[ 0 ] ) && !webos,
+ dynamicBaseTag: baseTagTest()
+});
+
+fakeBody.remove();
+
+
+// $.mobile.ajaxBlacklist is used to override ajaxEnabled on platforms that have known conflicts with hash history updates (BB5, Symbian)
+// or that generally work better browsing in regular http for full page refreshes (Opera Mini)
+// Note: This detection below is used as a last resort.
+// We recommend only using these detection methods when all other more reliable/forward-looking approaches are not possible
+var nokiaLTE7_3 = (function(){
+
+ var ua = window.navigator.userAgent;
+
+ //The following is an attempt to match Nokia browsers that are running Symbian/s60, with webkit, version 7.3 or older
+ return ua.indexOf( "Nokia" ) > -1 &&
+ ( ua.indexOf( "Symbian/3" ) > -1 || ua.indexOf( "Series60/5" ) > -1 ) &&
+ ua.indexOf( "AppleWebKit" ) > -1 &&
+ ua.match( /(BrowserNG|NokiaBrowser)\/7\.[0-3]/ );
+})();
+
+$.mobile.ajaxBlacklist =
+ // BlackBerry browsers, pre-webkit
+ window.blackberry && !window.WebKitPoint ||
+ // Opera Mini
+ window.operamini && Object.prototype.toString.call( window.operamini ) === "[object OperaMini]" ||
+ // Symbian webkits pre 7.3
+ nokiaLTE7_3;
+
+// Lastly, this workaround is the only way we've found so far to get pre 7.3 Symbian webkit devices
+// to render the stylesheets when they're referenced before this script, as we'd recommend doing.
+// This simply reappends the CSS in place, which for some reason makes it apply
+if ( nokiaLTE7_3 ) {
+ $(function() {
+ $( "head link[rel=stylesheet]" ).attr( "rel", "alternate stylesheet" ).attr( "rel", "stylesheet" );
+ });
+}
+
+// For ruling out shadows via css
+if ( !$.support.boxShadow ) {
+ $( "html" ).addClass( "ui-mobile-nosupport-boxshadow" );
+}
+
+})( jQuery );/*
+* jQuery Mobile Framework : "mouse" plugin
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+
+// This plugin is an experiment for abstracting away the touch and mouse
+// events so that developers don't have to worry about which method of input
+// the device their document is loaded on supports.
+//
+// The idea here is to allow the developer to register listeners for the
+// basic mouse events, such as mousedown, mousemove, mouseup, and click,
+// and the plugin will take care of registering the correct listeners
+// behind the scenes to invoke the listener at the fastest possible time
+// for that device, while still retaining the order of event firing in
+// the traditional mouse environment, should multiple handlers be registered
+// on the same element for different events.
+//
+// The current version exposes the following virtual events to jQuery bind methods:
+// "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel"
+
+(function( $, window, document, undefined ) {
+
+var dataPropertyName = "virtualMouseBindings",
+ touchTargetPropertyName = "virtualTouchID",
+ virtualEventNames = "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split( " " ),
+ touchEventProps = "clientX clientY pageX pageY screenX screenY".split( " " ),
+ activeDocHandlers = {},
+ resetTimerID = 0,
+ startX = 0,
+ startY = 0,
+ didScroll = false,
+ clickBlockList = [],
+ blockMouseTriggers = false,
+ blockTouchTriggers = false,
+ eventCaptureSupported = "addEventListener" in document,
+ $document = $( document ),
+ nextTouchID = 1,
+ lastTouchID = 0;
+
+$.vmouse = {
+ moveDistanceThreshold: 10,
+ clickDistanceThreshold: 10,
+ resetTimerDuration: 1500
+};
+
+function getNativeEvent( event ) {
+
+ while ( event && typeof event.originalEvent !== "undefined" ) {
+ event = event.originalEvent;
+ }
+ return event;
+}
+
+function createVirtualEvent( event, eventType ) {
+
+ var t = event.type,
+ oe, props, ne, prop, ct, touch, i, j;
+
+ event = $.Event(event);
+ event.type = eventType;
+
+ oe = event.originalEvent;
+ props = $.event.props;
+
+ // copy original event properties over to the new event
+ // this would happen if we could call $.event.fix instead of $.Event
+ // but we don't have a way to force an event to be fixed multiple times
+ if ( oe ) {
+ for ( i = props.length, prop; i; ) {
+ prop = props[ --i ];
+ event[ prop ] = oe[ prop ];
+ }
+ }
+
+ // make sure that if the mouse and click virtual events are generated
+ // without a .which one is defined
+ if ( t.search(/mouse(down|up)|click/) > -1 && !event.which ){
+ event.which = 1;
+ }
+
+ if ( t.search(/^touch/) !== -1 ) {
+ ne = getNativeEvent( oe );
+ t = ne.touches;
+ ct = ne.changedTouches;
+ touch = ( t && t.length ) ? t[0] : ( (ct && ct.length) ? ct[ 0 ] : undefined );
+
+ if ( touch ) {
+ for ( j = 0, len = touchEventProps.length; j < len; j++){
+ prop = touchEventProps[ j ];
+ event[ prop ] = touch[ prop ];
+ }
+ }
+ }
+
+ return event;
+}
+
+function getVirtualBindingFlags( element ) {
+
+ var flags = {},
+ b, k;
+
+ while ( element ) {
+
+ b = $.data( element, dataPropertyName );
+
+ for ( k in b ) {
+ if ( b[ k ] ) {
+ flags[ k ] = flags.hasVirtualBinding = true;
+ }
+ }
+ element = element.parentNode;
+ }
+ return flags;
+}
+
+function getClosestElementWithVirtualBinding( element, eventType ) {
+ var b;
+ while ( element ) {
+
+ b = $.data( element, dataPropertyName );
+
+ if ( b && ( !eventType || b[ eventType ] ) ) {
+ return element;
+ }
+ element = element.parentNode;
+ }
+ return null;
+}
+
+function enableTouchBindings() {
+ blockTouchTriggers = false;
+}
+
+function disableTouchBindings() {
+ blockTouchTriggers = true;
+}
+
+function enableMouseBindings() {
+ lastTouchID = 0;
+ clickBlockList.length = 0;
+ blockMouseTriggers = false;
+
+ // When mouse bindings are enabled, our
+ // touch bindings are disabled.
+ disableTouchBindings();
+}
+
+function disableMouseBindings() {
+ // When mouse bindings are disabled, our
+ // touch bindings are enabled.
+ enableTouchBindings();
+}
+
+function startResetTimer() {
+ clearResetTimer();
+ resetTimerID = setTimeout(function(){
+ resetTimerID = 0;
+ enableMouseBindings();
+ }, $.vmouse.resetTimerDuration );
+}
+
+function clearResetTimer() {
+ if ( resetTimerID ){
+ clearTimeout( resetTimerID );
+ resetTimerID = 0;
+ }
+}
+
+function triggerVirtualEvent( eventType, event, flags ) {
+ var ve;
+
+ if ( ( flags && flags[ eventType ] ) ||
+ ( !flags && getClosestElementWithVirtualBinding( event.target, eventType ) ) ) {
+
+ ve = createVirtualEvent( event, eventType );
+
+ $( event.target).trigger( ve );
+ }
+
+ return ve;
+}
+
+function mouseEventCallback( event ) {
+ var touchID = $.data(event.target, touchTargetPropertyName);
+
+ if ( !blockMouseTriggers && ( !lastTouchID || lastTouchID !== touchID ) ){
+ var ve = triggerVirtualEvent( "v" + event.type, event );
+ if ( ve ) {
+ if ( ve.isDefaultPrevented() ) {
+ event.preventDefault();
+ }
+ if ( ve.isPropagationStopped() ) {
+ event.stopPropagation();
+ }
+ if ( ve.isImmediatePropagationStopped() ) {
+ event.stopImmediatePropagation();
+ }
+ }
+ }
+}
+
+function handleTouchStart( event ) {
+
+ var touches = getNativeEvent( event ).touches,
+ target, flags;
+
+ if ( touches && touches.length === 1 ) {
+
+ target = event.target;
+ flags = getVirtualBindingFlags( target );
+
+ if ( flags.hasVirtualBinding ) {
+
+ lastTouchID = nextTouchID++;
+ $.data( target, touchTargetPropertyName, lastTouchID );
+
+ clearResetTimer();
+
+ disableMouseBindings();
+ didScroll = false;
+
+ var t = getNativeEvent( event ).touches[ 0 ];
+ startX = t.pageX;
+ startY = t.pageY;
+
+ triggerVirtualEvent( "vmouseover", event, flags );
+ triggerVirtualEvent( "vmousedown", event, flags );
+ }
+ }
+}
+
+function handleScroll( event ) {
+ if ( blockTouchTriggers ) {
+ return;
+ }
+
+ if ( !didScroll ) {
+ triggerVirtualEvent( "vmousecancel", event, getVirtualBindingFlags( event.target ) );
+ }
+
+ didScroll = true;
+ startResetTimer();
+}
+
+function handleTouchMove( event ) {
+ if ( blockTouchTriggers ) {
+ return;
+ }
+
+ var t = getNativeEvent( event ).touches[ 0 ],
+ didCancel = didScroll,
+ moveThreshold = $.vmouse.moveDistanceThreshold;
+ didScroll = didScroll ||
+ ( Math.abs(t.pageX - startX) > moveThreshold ||
+ Math.abs(t.pageY - startY) > moveThreshold ),
+ flags = getVirtualBindingFlags( event.target );
+
+ if ( didScroll && !didCancel ) {
+ triggerVirtualEvent( "vmousecancel", event, flags );
+ }
+
+ triggerVirtualEvent( "vmousemove", event, flags );
+ startResetTimer();
+}
+
+function handleTouchEnd( event ) {
+ if ( blockTouchTriggers ) {
+ return;
+ }
+
+ disableTouchBindings();
+
+ var flags = getVirtualBindingFlags( event.target ),
+ t;
+ triggerVirtualEvent( "vmouseup", event, flags );
+
+ if ( !didScroll ) {
+ var ve = triggerVirtualEvent( "vclick", event, flags );
+ if ( ve && ve.isDefaultPrevented() ) {
+ // The target of the mouse events that follow the touchend
+ // event don't necessarily match the target used during the
+ // touch. This means we need to rely on coordinates for blocking
+ // any click that is generated.
+ t = getNativeEvent( event ).changedTouches[ 0 ];
+ clickBlockList.push({
+ touchID: lastTouchID,
+ x: t.clientX,
+ y: t.clientY
+ });
+
+ // Prevent any mouse events that follow from triggering
+ // virtual event notifications.
+ blockMouseTriggers = true;
+ }
+ }
+ triggerVirtualEvent( "vmouseout", event, flags);
+ didScroll = false;
+
+ startResetTimer();
+}
+
+function hasVirtualBindings( ele ) {
+ var bindings = $.data( ele, dataPropertyName ),
+ k;
+
+ if ( bindings ) {
+ for ( k in bindings ) {
+ if ( bindings[ k ] ) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+function dummyMouseHandler(){}
+
+function getSpecialEventObject( eventType ) {
+ var realType = eventType.substr( 1 );
+
+ return {
+ setup: function( data, namespace ) {
+ // If this is the first virtual mouse binding for this element,
+ // add a bindings object to its data.
+
+ if ( !hasVirtualBindings( this ) ) {
+ $.data( this, dataPropertyName, {});
+ }
+
+ // If setup is called, we know it is the first binding for this
+ // eventType, so initialize the count for the eventType to zero.
+ var bindings = $.data( this, dataPropertyName );
+ bindings[ eventType ] = true;
+
+ // If this is the first virtual mouse event for this type,
+ // register a global handler on the document.
+
+ activeDocHandlers[ eventType ] = ( activeDocHandlers[ eventType ] || 0 ) + 1;
+
+ if ( activeDocHandlers[ eventType ] === 1 ) {
+ $document.bind( realType, mouseEventCallback );
+ }
+
+ // Some browsers, like Opera Mini, won't dispatch mouse/click events
+ // for elements unless they actually have handlers registered on them.
+ // To get around this, we register dummy handlers on the elements.
+
+ $( this ).bind( realType, dummyMouseHandler );
+
+ // For now, if event capture is not supported, we rely on mouse handlers.
+ if ( eventCaptureSupported ) {
+ // If this is the first virtual mouse binding for the document,
+ // register our touchstart handler on the document.
+
+ activeDocHandlers[ "touchstart" ] = ( activeDocHandlers[ "touchstart" ] || 0) + 1;
+
+ if (activeDocHandlers[ "touchstart" ] === 1) {
+ $document.bind( "touchstart", handleTouchStart )
+ .bind( "touchend", handleTouchEnd )
+
+ // On touch platforms, touching the screen and then dragging your finger
+ // causes the window content to scroll after some distance threshold is
+ // exceeded. On these platforms, a scroll prevents a click event from being
+ // dispatched, and on some platforms, even the touchend is suppressed. To
+ // mimic the suppression of the click event, we need to watch for a scroll
+ // event. Unfortunately, some platforms like iOS don't dispatch scroll
+ // events until *AFTER* the user lifts their finger (touchend). This means
+ // we need to watch both scroll and touchmove events to figure out whether
+ // or not a scroll happenens before the touchend event is fired.
+
+ .bind( "touchmove", handleTouchMove )
+ .bind( "scroll", handleScroll );
+ }
+ }
+ },
+
+ teardown: function( data, namespace ) {
+ // If this is the last virtual binding for this eventType,
+ // remove its global handler from the document.
+
+ --activeDocHandlers[ eventType ];
+
+ if ( !activeDocHandlers[ eventType ] ) {
+ $document.unbind( realType, mouseEventCallback );
+ }
+
+ if ( eventCaptureSupported ) {
+ // If this is the last virtual mouse binding in existence,
+ // remove our document touchstart listener.
+
+ --activeDocHandlers[ "touchstart" ];
+
+ if ( !activeDocHandlers[ "touchstart" ] ) {
+ $document.unbind( "touchstart", handleTouchStart )
+ .unbind( "touchmove", handleTouchMove )
+ .unbind( "touchend", handleTouchEnd )
+ .unbind( "scroll", handleScroll );
+ }
+ }
+
+ var $this = $( this ),
+ bindings = $.data( this, dataPropertyName );
+
+ // teardown may be called when an element was
+ // removed from the DOM. If this is the case,
+ // jQuery core may have already stripped the element
+ // of any data bindings so we need to check it before
+ // using it.
+ if ( bindings ) {
+ bindings[ eventType ] = false;
+ }
+
+ // Unregister the dummy event handler.
+
+ $this.unbind( realType, dummyMouseHandler );
+
+ // If this is the last virtual mouse binding on the
+ // element, remove the binding data from the element.
+
+ if ( !hasVirtualBindings( this ) ) {
+ $this.removeData( dataPropertyName );
+ }
+ }
+ };
+}
+
+// Expose our custom events to the jQuery bind/unbind mechanism.
+
+for ( var i = 0; i < virtualEventNames.length; i++ ){
+ $.event.special[ virtualEventNames[ i ] ] = getSpecialEventObject( virtualEventNames[ i ] );
+}
+
+// Add a capture click handler to block clicks.
+// Note that we require event capture support for this so if the device
+// doesn't support it, we punt for now and rely solely on mouse events.
+if ( eventCaptureSupported ) {
+ document.addEventListener( "click", function( e ){
+ var cnt = clickBlockList.length,
+ target = e.target,
+ x, y, ele, i, o, touchID;
+
+ if ( cnt ) {
+ x = e.clientX;
+ y = e.clientY;
+ threshold = $.vmouse.clickDistanceThreshold;
+
+ // The idea here is to run through the clickBlockList to see if
+ // the current click event is in the proximity of one of our
+ // vclick events that had preventDefault() called on it. If we find
+ // one, then we block the click.
+ //
+ // Why do we have to rely on proximity?
+ //
+ // Because the target of the touch event that triggered the vclick
+ // can be different from the target of the click event synthesized
+ // by the browser. The target of a mouse/click event that is syntehsized
+ // from a touch event seems to be implementation specific. For example,
+ // some browsers will fire mouse/click events for a link that is near
+ // a touch event, even though the target of the touchstart/touchend event
+ // says the user touched outside the link. Also, it seems that with most
+ // browsers, the target of the mouse/click event is not calculated until the
+ // time it is dispatched, so if you replace an element that you touched
+ // with another element, the target of the mouse/click will be the new
+ // element underneath that point.
+ //
+ // Aside from proximity, we also check to see if the target and any
+ // of its ancestors were the ones that blocked a click. This is necessary
+ // because of the strange mouse/click target calculation done in the
+ // Android 2.1 browser, where if you click on an element, and there is a
+ // mouse/click handler on one of its ancestors, the target will be the
+ // innermost child of the touched element, even if that child is no where
+ // near the point of touch.
+
+ ele = target;
+
+ while ( ele ) {
+ for ( i = 0; i < cnt; i++ ) {
+ o = clickBlockList[ i ];
+ touchID = 0;
+
+ if ( ( ele === target && Math.abs( o.x - x ) < threshold && Math.abs( o.y - y ) < threshold ) ||
+ $.data( ele, touchTargetPropertyName ) === o.touchID ) {
+ // XXX: We may want to consider removing matches from the block list
+ // instead of waiting for the reset timer to fire.
+ e.preventDefault();
+ e.stopPropagation();
+ return;
+ }
+ }
+ ele = ele.parentNode;
+ }
+ }
+ }, true);
+}
+})( jQuery, window, document );
+/*
+* jQuery Mobile Framework : events
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+(function( $, window, undefined ) {
+
+// add new event shortcuts
+$.each( ( "touchstart touchmove touchend orientationchange throttledresize " +
+ "tap taphold swipe swipeleft swiperight scrollstart scrollstop" ).split( " " ), function( i, name ) {
+
+ $.fn[ name ] = function( fn ) {
+ return fn ? this.bind( name, fn ) : this.trigger( name );
+ };
+
+ $.attrFn[ name ] = true;
+});
+
+var supportTouch = $.support.touch,
+ scrollEvent = "touchmove scroll",
+ touchStartEvent = supportTouch ? "touchstart" : "mousedown",
+ touchStopEvent = supportTouch ? "touchend" : "mouseup",
+ touchMoveEvent = supportTouch ? "touchmove" : "mousemove";
+
+function triggerCustomEvent( obj, eventType, event ) {
+ var originalType = event.type;
+ event.type = eventType;
+ $.event.handle.call( obj, event );
+ event.type = originalType;
+}
+
+// also handles scrollstop
+$.event.special.scrollstart = {
+
+ enabled: true,
+
+ setup: function() {
+
+ var thisObject = this,
+ $this = $( thisObject ),
+ scrolling,
+ timer;
+
+ function trigger( event, state ) {
+ scrolling = state;
+ triggerCustomEvent( thisObject, scrolling ? "scrollstart" : "scrollstop", event );
+ }
+
+ // iPhone triggers scroll after a small delay; use touchmove instead
+ $this.bind( scrollEvent, function( event ) {
+
+ if ( !$.event.special.scrollstart.enabled ) {
+ return;
+ }
+
+ if ( !scrolling ) {
+ trigger( event, true );
+ }
+
+ clearTimeout( timer );
+ timer = setTimeout(function() {
+ trigger( event, false );
+ }, 50 );
+ });
+ }
+};
+
+// also handles taphold
+$.event.special.tap = {
+ setup: function() {
+ var thisObject = this,
+ $this = $( thisObject );
+
+ $this.bind( "vmousedown", function( event ) {
+
+ if ( event.which && event.which !== 1 ) {
+ return false;
+ }
+
+ var origTarget = event.target,
+ origEvent = event.originalEvent,
+ timer;
+
+ function clearTapTimer() {
+ clearTimeout( timer );
+ }
+
+ function clearTapHandlers() {
+ clearTapTimer();
+
+ $this.unbind( "vclick", clickHandler )
+ .unbind( "vmouseup", clearTapTimer )
+ .unbind( "vmousecancel", clearTapHandlers );
+ }
+
+ function clickHandler(event) {
+ clearTapHandlers();
+
+ // ONLY trigger a 'tap' event if the start target is
+ // the same as the stop target.
+ if ( origTarget == event.target ) {
+ triggerCustomEvent( thisObject, "tap", event );
+ }
+ }
+
+ $this.bind( "vmousecancel", clearTapHandlers )
+ .bind( "vmouseup", clearTapTimer )
+ .bind( "vclick", clickHandler );
+
+ timer = setTimeout(function() {
+ triggerCustomEvent( thisObject, "taphold", $.Event( "taphold" ) );
+ }, 750 );
+ });
+ }
+};
+
+// also handles swipeleft, swiperight
+$.event.special.swipe = {
+ scrollSupressionThreshold: 10, // More than this horizontal displacement, and we will suppress scrolling.
+
+ durationThreshold: 1000, // More time than this, and it isn't a swipe.
+
+ horizontalDistanceThreshold: 30, // Swipe horizontal displacement must be more than this.
+
+ verticalDistanceThreshold: 75, // Swipe vertical displacement must be less than this.
+
+ setup: function() {
+ var thisObject = this,
+ $this = $( thisObject );
+
+ $this.bind( touchStartEvent, function( event ) {
+ var data = event.originalEvent.touches ?
+ event.originalEvent.touches[ 0 ] : event,
+ start = {
+ time: ( new Date() ).getTime(),
+ coords: [ data.pageX, data.pageY ],
+ origin: $( event.target )
+ },
+ stop;
+
+ function moveHandler( event ) {
+
+ if ( !start ) {
+ return;
+ }
+
+ var data = event.originalEvent.touches ?
+ event.originalEvent.touches[ 0 ] : event;
+
+ stop = {
+ time: ( new Date() ).getTime(),
+ coords: [ data.pageX, data.pageY ]
+ };
+
+ // prevent scrolling
+ if ( Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollSupressionThreshold ) {
+ event.preventDefault();
+ }
+ }
+
+ $this.bind( touchMoveEvent, moveHandler )
+ .one( touchStopEvent, function( event ) {
+ $this.unbind( touchMoveEvent, moveHandler );
+
+ if ( start && stop ) {
+ if ( stop.time - start.time < $.event.special.swipe.durationThreshold &&
+ Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.horizontalDistanceThreshold &&
+ Math.abs( start.coords[ 1 ] - stop.coords[ 1 ] ) < $.event.special.swipe.verticalDistanceThreshold ) {
+
+ start.origin.trigger( "swipe" )
+ .trigger( start.coords[0] > stop.coords[ 0 ] ? "swipeleft" : "swiperight" );
+ }
+ }
+ start = stop = undefined;
+ });
+ });
+ }
+};
+
+(function( $, window ) {
+ // "Cowboy" Ben Alman
+
+ var win = $( window ),
+ special_event,
+ get_orientation,
+ last_orientation;
+
+ $.event.special.orientationchange = special_event = {
+ setup: function() {
+ // If the event is supported natively, return false so that jQuery
+ // will bind to the event using DOM methods.
+ if ( $.support.orientation ) {
+ return false;
+ }
+
+ // Get the current orientation to avoid initial double-triggering.
+ last_orientation = get_orientation();
+
+ // Because the orientationchange event doesn't exist, simulate the
+ // event by testing window dimensions on resize.
+ win.bind( "throttledresize", handler );
+ },
+ teardown: function(){
+ // If the event is not supported natively, return false so that
+ // jQuery will unbind the event using DOM methods.
+ if ( $.support.orientation ) {
+ return false;
+ }
+
+ // Because the orientationchange event doesn't exist, unbind the
+ // resize event handler.
+ win.unbind( "throttledresize", handler );
+ },
+ add: function( handleObj ) {
+ // Save a reference to the bound event handler.
+ var old_handler = handleObj.handler;
+
+ handleObj.handler = function( event ) {
+ // Modify event object, adding the .orientation property.
+ event.orientation = get_orientation();
+
+ // Call the originally-bound event handler and return its result.
+ return old_handler.apply( this, arguments );
+ };
+ }
+ };
+
+ // If the event is not supported natively, this handler will be bound to
+ // the window resize event to simulate the orientationchange event.
+ function handler() {
+ // Get the current orientation.
+ var orientation = get_orientation();
+
+ if ( orientation !== last_orientation ) {
+ // The orientation has changed, so trigger the orientationchange event.
+ last_orientation = orientation;
+ win.trigger( "orientationchange" );
+ }
+ };
+
+ // Get the current page orientation. This method is exposed publicly, should it
+ // be needed, as jQuery.event.special.orientationchange.orientation()
+ $.event.special.orientationchange.orientation = get_orientation = function() {
+ var elem = document.documentElement;
+ return elem && elem.clientWidth / elem.clientHeight < 1.1 ? "portrait" : "landscape";
+ };
+
+})( jQuery, window );
+
+
+// throttled resize event
+(function() {
+
+ $.event.special.throttledresize = {
+ setup: function() {
+ $( this ).bind( "resize", handler );
+ },
+ teardown: function(){
+ $( this ).unbind( "resize", handler );
+ }
+ };
+
+ var throttle = 250,
+ handler = function() {
+ curr = ( new Date() ).getTime();
+ diff = curr - lastCall;
+
+ if ( diff >= throttle ) {
+
+ lastCall = curr;
+ $( this ).trigger( "throttledresize" );
+
+ } else {
+
+ if ( heldCall ) {
+ clearTimeout( heldCall );
+ }
+
+ // Promise a held call will still execute
+ heldCall = setTimeout( handler, throttle - diff );
+ }
+ },
+ lastCall = 0,
+ heldCall,
+ curr,
+ diff;
+})();
+
+
+$.each({
+ scrollstop: "scrollstart",
+ taphold: "tap",
+ swipeleft: "swipe",
+ swiperight: "swipe"
+}, function( event, sourceEvent ) {
+
+ $.event.special[ event ] = {
+ setup: function() {
+ $( this ).bind( sourceEvent, $.noop );
+ }
+ };
+});
+
+})( jQuery, this );
+/*!
+ * jQuery hashchange event - v1.3 - 7/21/2010
+ * http://benalman.com/projects/jquery-hashchange-plugin/
+ *
+ * Copyright (c) 2010 "Cowboy" Ben Alman
+ * Dual licensed under the MIT and GPL licenses.
+ * http://benalman.com/about/license/
+ */
+
+// Script: jQuery hashchange event
+//
+// *Version: 1.3, Last updated: 7/21/2010*
+//
+// Project Home - http://benalman.com/projects/jquery-hashchange-plugin/
+// GitHub - http://github.com/cowboy/jquery-hashchange/
+// Source - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js
+// (Minified) - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (0.8kb gzipped)
+//
+// About: License
+//
+// Copyright (c) 2010 "Cowboy" Ben Alman,
+// Dual licensed under the MIT and GPL licenses.
+// http://benalman.com/about/license/
+//
+// About: Examples
+//
+// These working examples, complete with fully commented code, illustrate a few
+// ways in which this plugin can be used.
+//
+// hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/
+// document.domain - http://benalman.com/code/projects/jquery-hashchange/examples/document_domain/
+//
+// About: Support and Testing
+//
+// Information about what version or versions of jQuery this plugin has been
+// tested with, what browsers it has been tested in, and where the unit tests
+// reside (so you can test it yourself).
+//
+// jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2
+// Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5,
+// Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5.
+// Unit Tests - http://benalman.com/code/projects/jquery-hashchange/unit/
+//
+// About: Known issues
+//
+// While this jQuery hashchange event implementation is quite stable and
+// robust, there are a few unfortunate browser bugs surrounding expected
+// hashchange event-based behaviors, independent of any JavaScript
+// window.onhashchange abstraction. See the following examples for more
+// information:
+//
+// Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/
+// Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/
+// WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/
+// Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/
+//
+// Also note that should a browser natively support the window.onhashchange
+// event, but not report that it does, the fallback polling loop will be used.
+//
+// About: Release History
+//
+// 1.3 - (7/21/2010) Reorganized IE6/7 Iframe code to make it more
+// "removable" for mobile-only development. Added IE6/7 document.title
+// support. Attempted to make Iframe as hidden as possible by using
+// techniques from http://www.paciellogroup.com/blog/?p=604. Added
+// support for the "shortcut" format $(window).hashchange( fn ) and
+// $(window).hashchange() like jQuery provides for built-in events.
+// Renamed jQuery.hashchangeDelay to <jQuery.fn.hashchange.delay> and
+// lowered its default value to 50. Added <jQuery.fn.hashchange.domain>
+// and <jQuery.fn.hashchange.src> properties plus document-domain.html
+// file to address access denied issues when setting document.domain in
+// IE6/7.
+// 1.2 - (2/11/2010) Fixed a bug where coming back to a page using this plugin
+// from a page on another domain would cause an error in Safari 4. Also,
+// IE6/7 Iframe is now inserted after the body (this actually works),
+// which prevents the page from scrolling when the event is first bound.
+// Event can also now be bound before DOM ready, but it won't be usable
+// before then in IE6/7.
+// 1.1 - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug
+// where browser version is incorrectly reported as 8.0, despite
+// inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag.
+// 1.0 - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special
+// window.onhashchange functionality into a separate plugin for users
+// who want just the basic event & back button support, without all the
+// extra awesomeness that BBQ provides. This plugin will be included as
+// part of jQuery BBQ, but also be available separately.
+
+(function($,window,undefined){
+ '$:nomunge'; // Used by YUI compressor.
+
+ // Reused string.
+ var str_hashchange = 'hashchange',
+
+ // Method / object references.
+ doc = document,
+ fake_onhashchange,
+ special = $.event.special,
+
+ // Does the browser support window.onhashchange? Note that IE8 running in
+ // IE7 compatibility mode reports true for 'onhashchange' in window, even
+ // though the event isn't supported, so also test document.documentMode.
+ doc_mode = doc.documentMode,
+ supports_onhashchange = 'on' + str_hashchange in window && ( doc_mode === undefined || doc_mode > 7 );
+
+ // Get location.hash (or what you'd expect location.hash to be) sans any
+ // leading #. Thanks for making this necessary, Firefox!
+ function get_fragment( url ) {
+ url = url || location.href;
+ return '#' + url.replace( /^[^#]*#?(.*)$/, '$1' );
+ };
+
+ // Method: jQuery.fn.hashchange
+ //
+ // Bind a handler to the window.onhashchange event or trigger all bound
+ // window.onhashchange event handlers. This behavior is consistent with
+ // jQuery's built-in event handlers.
+ //
+ // Usage:
+ //
+ // > jQuery(window).hashchange( [ handler ] );
+ //
+ // Arguments:
+ //
+ // handler - (Function) Optional handler to be bound to the hashchange
+ // event. This is a "shortcut" for the more verbose form:
+ // jQuery(window).bind( 'hashchange', handler ). If handler is omitted,
+ // all bound window.onhashchange event handlers will be triggered. This
+ // is a shortcut for the more verbose
+ // jQuery(window).trigger( 'hashchange' ). These forms are described in
+ // the <hashchange event> section.
+ //
+ // Returns:
+ //
+ // (jQuery) The initial jQuery collection of elements.
+
+ // Allow the "shortcut" format $(elem).hashchange( fn ) for binding and
+ // $(elem).hashchange() for triggering, like jQuery does for built-in events.
+ $.fn[ str_hashchange ] = function( fn ) {
+ return fn ? this.bind( str_hashchange, fn ) : this.trigger( str_hashchange );
+ };
+
+ // Property: jQuery.fn.hashchange.delay
+ //
+ // The numeric interval (in milliseconds) at which the <hashchange event>
+ // polling loop executes. Defaults to 50.
+
+ // Property: jQuery.fn.hashchange.domain
+ //
+ // If you're setting document.domain in your JavaScript, and you want hash
+ // history to work in IE6/7, not only must this property be set, but you must
+ // also set document.domain BEFORE jQuery is loaded into the page. This
+ // property is only applicable if you are supporting IE6/7 (or IE8 operating
+ // in "IE7 compatibility" mode).
+ //
+ // In addition, the <jQuery.fn.hashchange.src> property must be set to the
+ // path of the included "document-domain.html" file, which can be renamed or
+ // modified if necessary (note that the document.domain specified must be the
+ // same in both your main JavaScript as well as in this file).
+ //
+ // Usage:
+ //
+ // jQuery.fn.hashchange.domain = document.domain;
+
+ // Property: jQuery.fn.hashchange.src
+ //
+ // If, for some reason, you need to specify an Iframe src file (for example,
+ // when setting document.domain as in <jQuery.fn.hashchange.domain>), you can
+ // do so using this property. Note that when using this property, history
+ // won't be recorded in IE6/7 until the Iframe src file loads. This property
+ // is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7
+ // compatibility" mode).
+ //
+ // Usage:
+ //
+ // jQuery.fn.hashchange.src = 'path/to/file.html';
+
+ $.fn[ str_hashchange ].delay = 50;
+ /*
+ $.fn[ str_hashchange ].domain = null;
+ $.fn[ str_hashchange ].src = null;
+ */
+
+ // Event: hashchange event
+ //
+ // Fired when location.hash changes. In browsers that support it, the native
+ // HTML5 window.onhashchange event is used, otherwise a polling loop is
+ // initialized, running every <jQuery.fn.hashchange.delay> milliseconds to
+ // see if the hash has changed. In IE6/7 (and IE8 operating in "IE7
+ // compatibility" mode), a hidden Iframe is created to allow the back button
+ // and hash-based history to work.
+ //
+ // Usage as described in <jQuery.fn.hashchange>:
+ //
+ // > // Bind an event handler.
+ // > jQuery(window).hashchange( function(e) {
+ // > var hash = location.hash;
+ // > ...
+ // > });
+ // >
+ // > // Manually trigger the event handler.
+ // > jQuery(window).hashchange();
+ //
+ // A more verbose usage that allows for event namespacing:
+ //
+ // > // Bind an event handler.
+ // > jQuery(window).bind( 'hashchange', function(e) {
+ // > var hash = location.hash;
+ // > ...
+ // > });
+ // >
+ // > // Manually trigger the event handler.
+ // > jQuery(window).trigger( 'hashchange' );
+ //
+ // Additional Notes:
+ //
+ // * The polling loop and Iframe are not created until at least one handler
+ // is actually bound to the 'hashchange' event.
+ // * If you need the bound handler(s) to execute immediately, in cases where
+ // a location.hash exists on page load, via bookmark or page refresh for
+ // example, use jQuery(window).hashchange() or the more verbose
+ // jQuery(window).trigger( 'hashchange' ).
+ // * The event can be bound before DOM ready, but since it won't be usable
+ // before then in IE6/7 (due to the necessary Iframe), recommended usage is
+ // to bind it inside a DOM ready handler.
+
+ // Override existing $.event.special.hashchange methods (allowing this plugin
+ // to be defined after jQuery BBQ in BBQ's source code).
+ special[ str_hashchange ] = $.extend( special[ str_hashchange ], {
+
+ // Called only when the first 'hashchange' event is bound to window.
+ setup: function() {
+ // If window.onhashchange is supported natively, there's nothing to do..
+ if ( supports_onhashchange ) { return false; }
+
+ // Otherwise, we need to create our own. And we don't want to call this
+ // until the user binds to the event, just in case they never do, since it
+ // will create a polling loop and possibly even a hidden Iframe.
+ $( fake_onhashchange.start );
+ },
+
+ // Called only when the last 'hashchange' event is unbound from window.
+ teardown: function() {
+ // If window.onhashchange is supported natively, there's nothing to do..
+ if ( supports_onhashchange ) { return false; }
+
+ // Otherwise, we need to stop ours (if possible).
+ $( fake_onhashchange.stop );
+ }
+
+ });
+
+ // fake_onhashchange does all the work of triggering the window.onhashchange
+ // event for browsers that don't natively support it, including creating a
+ // polling loop to watch for hash changes and in IE 6/7 creating a hidden
+ // Iframe to enable back and forward.
+ fake_onhashchange = (function(){
+ var self = {},
+ timeout_id,
+
+ // Remember the initial hash so it doesn't get triggered immediately.
+ last_hash = get_fragment(),
+
+ fn_retval = function(val){ return val; },
+ history_set = fn_retval,
+ history_get = fn_retval;
+
+ // Start the polling loop.
+ self.start = function() {
+ timeout_id || poll();
+ };
+
+ // Stop the polling loop.
+ self.stop = function() {
+ timeout_id && clearTimeout( timeout_id );
+ timeout_id = undefined;
+ };
+
+ // This polling loop checks every $.fn.hashchange.delay milliseconds to see
+ // if location.hash has changed, and triggers the 'hashchange' event on
+ // window when necessary.
+ function poll() {
+ var hash = get_fragment(),
+ history_hash = history_get( last_hash );
+
+ if ( hash !== last_hash ) {
+ history_set( last_hash = hash, history_hash );
+
+ $(window).trigger( str_hashchange );
+
+ } else if ( history_hash !== last_hash ) {
+ location.href = location.href.replace( /#.*/, '' ) + history_hash;
+ }
+
+ timeout_id = setTimeout( poll, $.fn[ str_hashchange ].delay );
+ };
+
+ // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+ // vvvvvvvvvvvvvvvvvvv REMOVE IF NOT SUPPORTING IE6/7/8 vvvvvvvvvvvvvvvvvvv
+ // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+ $.browser.msie && !supports_onhashchange && (function(){
+ // Not only do IE6/7 need the "magical" Iframe treatment, but so does IE8
+ // when running in "IE7 compatibility" mode.
+
+ var iframe,
+ iframe_src;
+
+ // When the event is bound and polling starts in IE 6/7, create a hidden
+ // Iframe for history handling.
+ self.start = function(){
+ if ( !iframe ) {
+ iframe_src = $.fn[ str_hashchange ].src;
+ iframe_src = iframe_src && iframe_src + get_fragment();
+
+ // Create hidden Iframe. Attempt to make Iframe as hidden as possible
+ // by using techniques from http://www.paciellogroup.com/blog/?p=604.
+ iframe = $('<iframe tabindex="-1" title="empty"/>').hide()
+
+ // When Iframe has completely loaded, initialize the history and
+ // start polling.
+ .one( 'load', function(){
+ iframe_src || history_set( get_fragment() );
+ poll();
+ })
+
+ // Load Iframe src if specified, otherwise nothing.
+ .attr( 'src', iframe_src || 'javascript:0' )
+
+ // Append Iframe after the end of the body to prevent unnecessary
+ // initial page scrolling (yes, this works).
+ .insertAfter( 'body' )[0].contentWindow;
+
+ // Whenever `document.title` changes, update the Iframe's title to
+ // prettify the back/next history menu entries. Since IE sometimes
+ // errors with "Unspecified error" the very first time this is set
+ // (yes, very useful) wrap this with a try/catch block.
+ doc.onpropertychange = function(){
+ try {
+ if ( event.propertyName === 'title' ) {
+ iframe.document.title = doc.title;
+ }
+ } catch(e) {}
+ };
+
+ }
+ };
+
+ // Override the "stop" method since an IE6/7 Iframe was created. Even
+ // if there are no longer any bound event handlers, the polling loop
+ // is still necessary for back/next to work at all!
+ self.stop = fn_retval;
+
+ // Get history by looking at the hidden Iframe's location.hash.
+ history_get = function() {
+ return get_fragment( iframe.location.href );
+ };
+
+ // Set a new history item by opening and then closing the Iframe
+ // document, *then* setting its location.hash. If document.domain has
+ // been set, update that as well.
+ history_set = function( hash, history_hash ) {
+ var iframe_doc = iframe.document,
+ domain = $.fn[ str_hashchange ].domain;
+
+ if ( hash !== history_hash ) {
+ // Update Iframe with any initial `document.title` that might be set.
+ iframe_doc.title = doc.title;
+
+ // Opening the Iframe's document after it has been closed is what
+ // actually adds a history entry.
+ iframe_doc.open();
+
+ // Set document.domain for the Iframe document as well, if necessary.
+ domain && iframe_doc.write( '<script>document.domain="' + domain + '"</script>' );
+
+ iframe_doc.close();
+
+ // Update the Iframe's hash, for great justice.
+ iframe.location.hash = hash;
+ }
+ };
+
+ })();
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ // ^^^^^^^^^^^^^^^^^^^ REMOVE IF NOT SUPPORTING IE6/7/8 ^^^^^^^^^^^^^^^^^^^
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ return self;
+ })();
+
+})(jQuery,this);
+/*
+* jQuery Mobile Framework : "page" plugin
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+
+(function( $, undefined ) {
+
+$.widget( "mobile.page", $.mobile.widget, {
+ options: {
+ theme: "c",
+ domCache: false
+ },
+
+ _create: function() {
+
+ this._trigger( "beforecreate" );
+
+ this.element
+ .attr( "tabindex", "0" )
+ .addClass( "ui-page ui-body-" + this.options.theme );
+ }
+});
+
+})( jQuery );
+/*!
+ * jQuery Mobile v@VERSION
+ * http://jquerymobile.com/
+ *
+ * Copyright 2010, jQuery Project
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ */
+
+(function( $, window, undefined ) {
+
+ // jQuery.mobile configurable options
+ $.extend( $.mobile, {
+
+ // Namespace used framework-wide for data-attrs. Default is no namespace
+ ns: "",
+
+ // Define the url parameter used for referencing widget-generated sub-pages.
+ // Translates to to example.html&ui-page=subpageIdentifier
+ // hash segment before &ui-page= is used to make Ajax request
+ subPageUrlKey: "ui-page",
+
+ // Class assigned to page currently in view, and during transitions
+ activePageClass: "ui-page-active",
+
+ // Class used for "active" button state, from CSS framework
+ activeBtnClass: "ui-btn-active",
+
+ // Automatically handle clicks and form submissions through Ajax, when same-domain
+ ajaxEnabled: true,
+
+ // Automatically load and show pages based on location.hash
+ hashListeningEnabled: true,
+
+ // Set default page transition - 'none' for no transitions
+ defaultPageTransition: "slide",
+
+ // Minimum scroll distance that will be remembered when returning to a page
+ minScrollBack: 250,
+
+ // Set default dialog transition - 'none' for no transitions
+ defaultDialogTransition: "pop",
+
+ // Show loading message during Ajax requests
+ // if false, message will not appear, but loading classes will still be toggled on html el
+ loadingMessage: "loading",
+
+ // Error response message - appears when an Ajax page request fails
+ pageLoadErrorMessage: "Error Loading Page",
+
+ //automatically initialize the DOM when it's ready
+ autoInitializePage: true,
+
+ pushStateEnabled: true,
+
+ // Support conditions that must be met in order to proceed
+ // default enhanced qualifications are media query support OR IE 7+
+ gradeA: function(){
+ return $.support.mediaquery || $.mobile.browser.ie && $.mobile.browser.ie >= 7;
+ },
+
+ // TODO might be useful upstream in jquery itself ?
+ keyCode: {
+ ALT: 18,
+ BACKSPACE: 8,
+ CAPS_LOCK: 20,
+ COMMA: 188,
+ COMMAND: 91,
+ COMMAND_LEFT: 91, // COMMAND
+ COMMAND_RIGHT: 93,
+ CONTROL: 17,
+ DELETE: 46,
+ DOWN: 40,
+ END: 35,
+ ENTER: 13,
+ ESCAPE: 27,
+ HOME: 36,
+ INSERT: 45,
+ LEFT: 37,
+ MENU: 93, // COMMAND_RIGHT
+ NUMPAD_ADD: 107,
+ NUMPAD_DECIMAL: 110,
+ NUMPAD_DIVIDE: 111,
+ NUMPAD_ENTER: 108,
+ NUMPAD_MULTIPLY: 106,
+ NUMPAD_SUBTRACT: 109,
+ PAGE_DOWN: 34,
+ PAGE_UP: 33,
+ PERIOD: 190,
+ RIGHT: 39,
+ SHIFT: 16,
+ SPACE: 32,
+ TAB: 9,
+ UP: 38,
+ WINDOWS: 91 // COMMAND
+ },
+
+ // Scroll page vertically: scroll to 0 to hide iOS address bar, or pass a Y value
+ silentScroll: function( ypos ) {
+ if ( $.type( ypos ) !== "number" ) {
+ ypos = $.mobile.defaultHomeScroll;
+ }
+
+ // prevent scrollstart and scrollstop events
+ $.event.special.scrollstart.enabled = false;
+
+ setTimeout(function() {
+ window.scrollTo( 0, ypos );
+ $( document ).trigger( "silentscroll", { x: 0, y: ypos });
+ }, 20 );
+
+ setTimeout(function() {
+ $.event.special.scrollstart.enabled = true;
+ }, 150 );
+ },
+
+ // Take a data attribute property, prepend the namespace
+ // and then camel case the attribute string
+ nsNormalize: function( prop ) {
+ if ( !prop ) {
+ return;
+ }
+
+ return $.camelCase( $.mobile.ns + prop );
+ }
+ });
+
+ // Mobile version of data and removeData and hasData methods
+ // ensures all data is set and retrieved using jQuery Mobile's data namespace
+ $.fn.jqmData = function( prop, value ) {
+ var result;
+ if ( typeof prop != "undefined" ) {
+ result = this.data( prop ? $.mobile.nsNormalize( prop ) : prop, value );
+ }
+ return result;
+ };
+
+ $.jqmData = function( elem, prop, value ) {
+ var result;
+ if ( typeof prop != "undefined" ) {
+ result = $.data( elem, prop ? $.mobile.nsNormalize( prop ) : prop, value );
+ }
+ return result;
+ };
+
+ $.fn.jqmRemoveData = function( prop ) {
+ return this.removeData( $.mobile.nsNormalize( prop ) );
+ };
+
+ $.jqmRemoveData = function( elem, prop ) {
+ return $.removeData( elem, $.mobile.nsNormalize( prop ) );
+ };
+
+ $.fn.removeWithDependents = function() {
+ $.removeWithDependents( this );
+ };
+
+ $.removeWithDependents = function( elem ) {
+ var $elem = $( elem );
+
+ ( $elem.jqmData('dependents') || $() ).remove();
+ $elem.remove();
+ };
+
+ $.fn.addDependents = function( newDependents ) {
+ $.addDependents( $(this), newDependents );
+ };
+
+ $.addDependents = function( elem, newDependents ) {
+ var dependents = $(elem).jqmData( 'dependents' ) || $();
+
+ $(elem).jqmData( 'dependents', $.merge(dependents, newDependents) );
+ };
+
+ // note that this helper doesn't attempt to handle the callback
+ // or setting of an html elements text, its only purpose is
+ // to return the html encoded version of the text in all cases. (thus the name)
+ $.fn.getEncodedText = function() {
+ return $( "<div/>" ).text( $(this).text() ).html();
+ };
+
+ // Monkey-patching Sizzle to filter the :jqmData selector
+ var oldFind = $.find;
+
+ $.find = function( selector, context, ret, extra ) {
+ selector = selector.replace(/:jqmData\(([^)]*)\)/g, "[data-" + ( $.mobile.ns || "" ) + "$1]");
+
+ return oldFind.call( this, selector, context, ret, extra );
+ };
+
+ $.extend( $.find, oldFind );
+
+ $.find.matches = function( expr, set ) {
+ return $.find( expr, null, null, set );
+ };
+
+ $.find.matchesSelector = function( node, expr ) {
+ return $.find( expr, null, null, [ node ] ).length > 0;
+ };
+})( jQuery, this );
+
+/*
+* jQuery Mobile Framework : core utilities for auto ajax navigation, base tag mgmt,
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+( function( $, undefined ) {
+
+ //define vars for interal use
+ var $window = $( window ),
+ $html = $( 'html' ),
+ $head = $( 'head' ),
+
+ //url path helpers for use in relative url management
+ path = {
+
+ // This scary looking regular expression parses an absolute URL or its relative
+ // variants (protocol, site, document, query, and hash), into the various
+ // components (protocol, host, path, query, fragment, etc that make up the
+ // URL as well as some other commonly used sub-parts. When used with RegExp.exec()
+ // or String.match, it parses the URL into a results array that looks like this:
+ //
+ // [0]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread#msg-content
+ // [1]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread
+ // [2]: http://jblas:password@mycompany.com:8080/mail/inbox
+ // [3]: http://jblas:password@mycompany.com:8080
+ // [4]: http:
+ // [5]: //
+ // [6]: jblas:password@mycompany.com:8080
+ // [7]: jblas:password
+ // [8]: jblas
+ // [9]: password
+ // [10]: mycompany.com:8080
+ // [11]: mycompany.com
+ // [12]: 8080
+ // [13]: /mail/inbox
+ // [14]: /mail/
+ // [15]: inbox
+ // [16]: ?msg=1234&type=unread
+ // [17]: #msg-content
+ //
+ urlParseRE: /^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,
+
+ //Parse a URL into a structure that allows easy access to
+ //all of the URL components by name.
+ parseUrl: function( url ) {
+ // If we're passed an object, we'll assume that it is
+ // a parsed url object and just return it back to the caller.
+ if ( $.type( url ) === "object" ) {
+ return url;
+ }
+
+ var matches = path.urlParseRE.exec( url || "" ) || [];
+
+ // Create an object that allows the caller to access the sub-matches
+ // by name. Note that IE returns an empty string instead of undefined,
+ // like all other browsers do, so we normalize everything so its consistent
+ // no matter what browser we're running on.
+ return {
+ href: matches[ 0 ] || "",
+ hrefNoHash: matches[ 1 ] || "",
+ hrefNoSearch: matches[ 2 ] || "",
+ domain: matches[ 3 ] || "",
+ protocol: matches[ 4 ] || "",
+ doubleSlash: matches[ 5 ] || "",
+ authority: matches[ 6 ] || "",
+ username: matches[ 8 ] || "",
+ password: matches[ 9 ] || "",
+ host: matches[ 10 ] || "",
+ hostname: matches[ 11 ] || "",
+ port: matches[ 12 ] || "",
+ pathname: matches[ 13 ] || "",
+ directory: matches[ 14 ] || "",
+ filename: matches[ 15 ] || "",
+ search: matches[ 16 ] || "",
+ hash: matches[ 17 ] || ""
+ };
+ },
+
+ //Turn relPath into an asbolute path. absPath is
+ //an optional absolute path which describes what
+ //relPath is relative to.
+ makePathAbsolute: function( relPath, absPath ) {
+ if ( relPath && relPath.charAt( 0 ) === "/" ) {
+ return relPath;
+ }
+
+ relPath = relPath || "";
+ absPath = absPath ? absPath.replace( /^\/|(\/[^\/]*|[^\/]+)$/g, "" ) : "";
+
+ var absStack = absPath ? absPath.split( "/" ) : [],
+ relStack = relPath.split( "/" );
+ for ( var i = 0; i < relStack.length; i++ ) {
+ var d = relStack[ i ];
+ switch ( d ) {
+ case ".":
+ break;
+ case "..":
+ if ( absStack.length ) {
+ absStack.pop();
+ }
+ break;
+ default:
+ absStack.push( d );
+ break;
+ }
+ }
+ return "/" + absStack.join( "/" );
+ },
+
+ //Returns true if both urls have the same domain.
+ isSameDomain: function( absUrl1, absUrl2 ) {
+ return path.parseUrl( absUrl1 ).domain === path.parseUrl( absUrl2 ).domain;
+ },
+
+ //Returns true for any relative variant.
+ isRelativeUrl: function( url ) {
+ // All relative Url variants have one thing in common, no protocol.
+ return path.parseUrl( url ).protocol === "";
+ },
+
+ //Returns true for an absolute url.
+ isAbsoluteUrl: function( url ) {
+ return path.parseUrl( url ).protocol !== "";
+ },
+
+ //Turn the specified realtive URL into an absolute one. This function
+ //can handle all relative variants (protocol, site, document, query, fragment).
+ makeUrlAbsolute: function( relUrl, absUrl ) {
+ if ( !path.isRelativeUrl( relUrl ) ) {
+ return relUrl;
+ }
+
+ var relObj = path.parseUrl( relUrl ),
+ absObj = path.parseUrl( absUrl ),
+ protocol = relObj.protocol || absObj.protocol,
+ doubleSlash = relObj.protocol ? relObj.doubleSlash : ( relObj.doubleSlash || absObj.doubleSlash );
+ authority = relObj.authority || absObj.authority,
+ hasPath = relObj.pathname !== "",
+ pathname = path.makePathAbsolute( relObj.pathname || absObj.filename, absObj.pathname ),
+ search = relObj.search || ( !hasPath && absObj.search ) || "",
+ hash = relObj.hash;
+
+ return protocol + doubleSlash + authority + pathname + search + hash;
+ },
+
+ //Add search (aka query) params to the specified url.
+ addSearchParams: function( url, params ) {
+ var u = path.parseUrl( url ),
+ p = ( typeof params === "object" ) ? $.param( params ) : params,
+ s = u.search || "?";
+ return u.hrefNoSearch + s + ( s.charAt( s.length - 1 ) !== "?" ? "&" : "" ) + p + ( u.hash || "" );
+ },
+
+ convertUrlToDataUrl: function( absUrl ) {
+ var u = path.parseUrl( absUrl );
+ if ( path.isEmbeddedPage( u ) ) {
+ // For embedded pages, remove the dialog hash key as in getFilePath(),
+ // otherwise the Data Url won't match the id of the embedded Page.
+ return u.hash.split( dialogHashKey )[0].replace( /^#/, "" );
+ } else if ( path.isSameDomain( u, documentBase ) ) {
+ return u.hrefNoHash.replace( documentBase.domain, "" );
+ }
+ return absUrl;
+ },
+
+ //get path from current hash, or from a file path
+ get: function( newPath ) {
+ if( newPath === undefined ) {
+ newPath = location.hash;
+ }
+ return path.stripHash( newPath ).replace( /[^\/]*\.[^\/*]+$/, '' );
+ },
+
+ //return the substring of a filepath before the sub-page key, for making a server request
+ getFilePath: function( path ) {
+ var splitkey = '&' + $.mobile.subPageUrlKey;
+ return path && path.split( splitkey )[0].split( dialogHashKey )[0];
+ },
+
+ //set location hash to path
+ set: function( path ) {
+ location.hash = path;
+ },
+
+ //test if a given url (string) is a path
+ //NOTE might be exceptionally naive
+ isPath: function( url ) {
+ return ( /\// ).test( url );
+ },
+
+ //return a url path with the window's location protocol/hostname/pathname removed
+ clean: function( url ) {
+ return url.replace( documentBase.domain, "" );
+ },
+
+ //just return the url without an initial #
+ stripHash: function( url ) {
+ return url.replace( /^#/, "" );
+ },
+
+ //remove the preceding hash, any query params, and dialog notations
+ cleanHash: function( hash ) {
+ return path.stripHash( hash.replace( /\?.*$/, "" ).replace( dialogHashKey, "" ) );
+ },
+
+ //check whether a url is referencing the same domain, or an external domain or different protocol
+ //could be mailto, etc
+ isExternal: function( url ) {
+ var u = path.parseUrl( url );
+ return u.protocol && u.domain !== documentUrl.domain ? true : false;
+ },
+
+ hasProtocol: function( url ) {
+ return ( /^(:?\w+:)/ ).test( url );
+ },
+
+ //check if the specified url refers to the first page in the main application document.
+ isFirstPageUrl: function( url ) {
+ // We only deal with absolute paths.
+ var u = path.parseUrl( path.makeUrlAbsolute( url, documentBase ) ),
+
+ // Does the url have the same path as the document?
+ samePath = u.hrefNoHash === documentUrl.hrefNoHash || ( documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash ),
+
+ // Get the first page element.
+ fp = $.mobile.firstPage,
+
+ // Get the id of the first page element if it has one.
+ fpId = fp && fp[0] ? fp[0].id : undefined;
+
+ // The url refers to the first page if the path matches the document and
+ // it either has no hash value, or the hash is exactly equal to the id of the
+ // first page element.
+ return samePath && ( !u.hash || u.hash === "#" || ( fpId && u.hash.replace( /^#/, "" ) === fpId ) );
+ },
+
+ isEmbeddedPage: function( url ) {
+ var u = path.parseUrl( url );
+
+ //if the path is absolute, then we need to compare the url against
+ //both the documentUrl and the documentBase. The main reason for this
+ //is that links embedded within external documents will refer to the
+ //application document, whereas links embedded within the application
+ //document will be resolved against the document base.
+ if ( u.protocol !== "" ) {
+ return ( u.hash && ( u.hrefNoHash === documentUrl.hrefNoHash || ( documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash ) ) );
+ }
+ return (/^#/).test( u.href );
+ }
+ },
+
+ //will be defined when a link is clicked and given an active class
+ $activeClickedLink = null,
+
+ //urlHistory is purely here to make guesses at whether the back or forward button was clicked
+ //and provide an appropriate transition
+ urlHistory = {
+ // Array of pages that are visited during a single page load.
+ // Each has a url and optional transition, title, and pageUrl (which represents the file path, in cases where URL is obscured, such as dialogs)
+ stack: [],
+
+ //maintain an index number for the active page in the stack
+ activeIndex: 0,
+
+ //get active
+ getActive: function() {
+ return urlHistory.stack[ urlHistory.activeIndex ];
+ },
+
+ getPrev: function() {
+ return urlHistory.stack[ urlHistory.activeIndex - 1 ];
+ },
+
+ getNext: function() {
+ return urlHistory.stack[ urlHistory.activeIndex + 1 ];
+ },
+
+ // addNew is used whenever a new page is added
+ addNew: function( url, transition, title, pageUrl, role ) {
+ //if there's forward history, wipe it
+ if( urlHistory.getNext() ) {
+ urlHistory.clearForward();
+ }
+
+ urlHistory.stack.push( {url : url, transition: transition, title: title, pageUrl: pageUrl, role: role } );
+
+ urlHistory.activeIndex = urlHistory.stack.length - 1;
+ },
+
+ //wipe urls ahead of active index
+ clearForward: function() {
+ urlHistory.stack = urlHistory.stack.slice( 0, urlHistory.activeIndex + 1 );
+ },
+
+ directHashChange: function( opts ) {
+ var back , forward, newActiveIndex, prev = this.getActive();
+
+ // check if url isp in history and if it's ahead or behind current page
+ $.each( urlHistory.stack, function( i, historyEntry ) {
+
+ //if the url is in the stack, it's a forward or a back
+ if( opts.currentUrl === historyEntry.url ) {
+ //define back and forward by whether url is older or newer than current page
+ back = i < urlHistory.activeIndex;
+ forward = !back;
+ newActiveIndex = i;
+ }
+ });
+
+ // save new page index, null check to prevent falsey 0 result
+ this.activeIndex = newActiveIndex !== undefined ? newActiveIndex : this.activeIndex;
+
+ if( back ) {
+ ( opts.either || opts.isBack )( true );
+ } else if( forward ) {
+ ( opts.either || opts.isForward )( false );
+ }
+ },
+
+ //disable hashchange event listener internally to ignore one change
+ //toggled internally when location.hash is updated to match the url of a successful page load
+ ignoreNextHashChange: false
+ },
+
+ //define first selector to receive focus when a page is shown
+ focusable = "[tabindex],a,button:visible,select:visible,input",
+
+ //queue to hold simultanious page transitions
+ pageTransitionQueue = [],
+
+ //indicates whether or not page is in process of transitioning
+ isPageTransitioning = false,
+
+ //nonsense hash change key for dialogs, so they create a history entry
+ dialogHashKey = "&ui-state=dialog",
+
+ //existing base tag?
+ $base = $head.children( "base" ),
+
+ //tuck away the original document URL minus any fragment.
+ documentUrl = path.parseUrl( location.href ),
+
+ //if the document has an embedded base tag, documentBase is set to its
+ //initial value. If a base tag does not exist, then we default to the documentUrl.
+ documentBase = $base.length ? path.parseUrl( path.makeUrlAbsolute( $base.attr( "href" ), documentUrl.href ) ) : documentUrl,
+
+ //cache the comparison once.
+ documentBaseDiffers = ( documentUrl.hrefNoHash !== documentBase.hrefNoHash );
+
+ //base element management, defined depending on dynamic base tag support
+ var base = $.support.dynamicBaseTag ? {
+
+ //define base element, for use in routing asset urls that are referenced in Ajax-requested markup
+ element: ( $base.length ? $base : $( "<base>", { href: documentBase.hrefNoHash } ).prependTo( $head ) ),
+
+ //set the generated BASE element's href attribute to a new page's base path
+ set: function( href ) {
+ base.element.attr( "href", path.makeUrlAbsolute( href, documentBase ) );
+ },
+
+ //set the generated BASE element's href attribute to a new page's base path
+ reset: function() {
+ base.element.attr( "href", documentBase.hrefNoHash );
+ }
+
+ } : undefined;
+
+/*
+ internal utility functions
+--------------------------------------*/
+
+
+ //direct focus to the page title, or otherwise first focusable element
+ function reFocus( page ) {
+ var pageTitle = page.find( ".ui-title:eq(0)" );
+
+ if( pageTitle.length ) {
+ pageTitle.focus();
+ }
+ else{
+ page.focus();
+ }
+ }
+
+ //remove active classes after page transition or error
+ function removeActiveLinkClass( forceRemoval ) {
+ if( !!$activeClickedLink && ( !$activeClickedLink.closest( '.ui-page-active' ).length || forceRemoval ) ) {
+ $activeClickedLink.removeClass( $.mobile.activeBtnClass );
+ }
+ $activeClickedLink = null;
+ }
+
+ function releasePageTransitionLock() {
+ isPageTransitioning = false;
+ if( pageTransitionQueue.length > 0 ) {
+ $.mobile.changePage.apply( null, pageTransitionQueue.pop() );
+ }
+ }
+
+ // Save the last scroll distance per page, before it is hidden
+ var setLastScrollEnabled = true,
+ firstScrollElem, getScrollElem, setLastScroll, delayedSetLastScroll;
+
+ getScrollElem = function() {
+ var scrollElem = $window, activePage,
+ touchOverflow = $.support.touchOverflow && $.mobile.touchOverflowEnabled;
+
+ if( touchOverflow ){
+ activePage = $( ".ui-page-active" );
+ scrollElem = activePage.is( ".ui-native-fixed" ) ? activePage.find( ".ui-content" ) : activePage;
+ }
+
+ return scrollElem;
+ };
+
+ setLastScroll = function( scrollElem ) {
+ // this barrier prevents setting the scroll value based on the browser
+ // scrolling the window based on a hashchange
+ if( !setLastScrollEnabled ) {
+ return;
+ }
+
+ var active = $.mobile.urlHistory.getActive();
+
+ if( active ) {
+ var lastScroll = scrollElem && scrollElem.scrollTop();
+
+ // Set active page's lastScroll prop.
+ // If the location we're scrolling to is less than minScrollBack, let it go.
+ active.lastScroll = lastScroll < $.mobile.minScrollBack ? $.mobile.defaultHomeScroll : lastScroll;
+ }
+ };
+
+ // bind to scrollstop to gather scroll position. The delay allows for the hashchange
+ // event to fire and disable scroll recording in the case where the browser scrolls
+ // to the hash targets location (sometimes the top of the page). once pagechange fires
+ // getLastScroll is again permitted to operate
+ delayedSetLastScroll = function() {
+ setTimeout( setLastScroll, 100, $(this) );
+ };
+
+ // disable an scroll setting when a hashchange has been fired, this only works
+ // because the recording of the scroll position is delayed for 100ms after
+ // the browser might have changed the position because of the hashchange
+ $window.bind( $.support.pushState ? "popstate" : "hashchange", function() {
+ setLastScrollEnabled = false;
+ });
+
+ // handle initial hashchange from chrome :(
+ $window.one( $.support.pushState ? "popstate" : "hashchange", function() {
+ setLastScrollEnabled = true;
+ });
+
+ // wait until the mobile page container has been determined to bind to pagechange
+ $window.one( "pagecontainercreate", function(){
+ // once the page has changed, re-enable the scroll recording
+ $.mobile.pageContainer.bind( "pagechange", function() {
+ var scrollElem = getScrollElem();
+
+ setLastScrollEnabled = true;
+
+ // remove any binding that previously existed on the get scroll
+ // which may or may not be different than the scroll element determined for
+ // this page previously
+ scrollElem.unbind( "scrollstop", delayedSetLastScroll );
+
+ // determine and bind to the current scoll element which may be the window
+ // or in the case of touch overflow the element with touch overflow
+ scrollElem.bind( "scrollstop", delayedSetLastScroll );
+ });
+ });
+
+ // bind to scrollstop for the first page as "pagechange" won't be fired in that case
+ getScrollElem().bind( "scrollstop", delayedSetLastScroll );
+
+ // Make the iOS clock quick-scroll work again if we're using native overflow scrolling
+ /*
+ if( $.support.touchOverflow ){
+ if( $.mobile.touchOverflowEnabled ){
+ $( window ).bind( "scrollstop", function(){
+ if( $( this ).scrollTop() === 0 ){
+ $.mobile.activePage.scrollTop( 0 );
+ }
+ });
+ }
+ }
+ */
+
+ //function for transitioning between two existing pages
+ function transitionPages( toPage, fromPage, transition, reverse ) {
+
+ //get current scroll distance
+ var active = $.mobile.urlHistory.getActive(),
+ touchOverflow = $.support.touchOverflow && $.mobile.touchOverflowEnabled,
+ toScroll = active.lastScroll || ( touchOverflow ? 0 : $.mobile.defaultHomeScroll ),
+ screenHeight = getScreenHeight();
+
+ // Scroll to top, hide addr bar
+ window.scrollTo( 0, $.mobile.defaultHomeScroll );
+
+ if( fromPage ) {
+ //trigger before show/hide events
+ fromPage.data( "page" )._trigger( "beforehide", null, { nextPage: toPage } );
+ }
+
+ if( !touchOverflow){
+ toPage.height( screenHeight + toScroll );
+ }
+
+ toPage.data( "page" )._trigger( "beforeshow", null, { prevPage: fromPage || $( "" ) } );
+
+ //clear page loader
+ $.mobile.hidePageLoadingMsg();
+
+ if( touchOverflow && toScroll ){
+
+ toPage.addClass( "ui-mobile-pre-transition" );
+ // Send focus to page as it is now display: block
+ reFocus( toPage );
+
+ //set page's scrollTop to remembered distance
+ if( toPage.is( ".ui-native-fixed" ) ){
+ toPage.find( ".ui-content" ).scrollTop( toScroll );
+ }
+ else{
+ toPage.scrollTop( toScroll );
+ }
+ }
+
+ //find the transition handler for the specified transition. If there
+ //isn't one in our transitionHandlers dictionary, use the default one.
+ //call the handler immediately to kick-off the transition.
+ var th = $.mobile.transitionHandlers[transition || "none"] || $.mobile.defaultTransitionHandler,
+ promise = th( transition, reverse, toPage, fromPage );
+
+ promise.done(function() {
+ //reset toPage height back
+ if( !touchOverflow ){
+ toPage.height( "" );
+ // Send focus to the newly shown page
+ reFocus( toPage );
+ }
+
+ // Jump to top or prev scroll, sometimes on iOS the page has not rendered yet.
+ if( !touchOverflow ){
+ $.mobile.silentScroll( toScroll );
+ }
+
+ //trigger show/hide events
+ if( fromPage ) {
+ if( !touchOverflow ){
+ fromPage.height( "" );
+ }
+
+ fromPage.data( "page" )._trigger( "hide", null, { nextPage: toPage } );
+ }
+
+ //trigger pageshow, define prevPage as either fromPage or empty jQuery obj
+ toPage.data( "page" )._trigger( "show", null, { prevPage: fromPage || $( "" ) } );
+ });
+
+ return promise;
+ }
+
+ //simply set the active page's minimum height to screen height, depending on orientation
+ function getScreenHeight(){
+ var orientation = jQuery.event.special.orientationchange.orientation(),
+ port = orientation === "portrait",
+ winMin = port ? 480 : 320,
+ screenHeight = port ? screen.availHeight : screen.availWidth,
+ winHeight = Math.max( winMin, $( window ).height() ),
+ pageMin = Math.min( screenHeight, winHeight );
+
+ return pageMin;
+ }
+
+ $.mobile.getScreenHeight = getScreenHeight;
+
+ //simply set the active page's minimum height to screen height, depending on orientation
+ function resetActivePageHeight(){
+ // Don't apply this height in touch overflow enabled mode
+ if( $.support.touchOverflow && $.mobile.touchOverflowEnabled ){
+ return;
+ }
+ $( "." + $.mobile.activePageClass ).css( "min-height", getScreenHeight() );
+ }
+
+ //shared page enhancements
+ function enhancePage( $page, role ) {
+ // If a role was specified, make sure the data-role attribute
+ // on the page element is in sync.
+ if( role ) {
+ $page.attr( "data-" + $.mobile.ns + "role", role );
+ }
+
+ //run page plugin
+ $page.page();
+ }
+
+/* exposed $.mobile methods */
+
+ //animation complete callback
+ $.fn.animationComplete = function( callback ) {
+ if( $.support.cssTransitions ) {
+ return $( this ).one( 'webkitAnimationEnd', callback );
+ }
+ else{
+ // defer execution for consistency between webkit/non webkit
+ setTimeout( callback, 0 );
+ return $( this );
+ }
+ };
+
+ //expose path object on $.mobile
+ $.mobile.path = path;
+
+ //expose base object on $.mobile
+ $.mobile.base = base;
+
+ //history stack
+ $.mobile.urlHistory = urlHistory;
+
+ $.mobile.dialogHashKey = dialogHashKey;
+
+ //default non-animation transition handler
+ $.mobile.noneTransitionHandler = function( name, reverse, $toPage, $fromPage ) {
+ if ( $fromPage ) {
+ $fromPage.removeClass( $.mobile.activePageClass );
+ }
+ $toPage.addClass( $.mobile.activePageClass );
+
+ return $.Deferred().resolve( name, reverse, $toPage, $fromPage ).promise();
+ };
+
+ //default handler for unknown transitions
+ $.mobile.defaultTransitionHandler = $.mobile.noneTransitionHandler;
+
+ //transition handler dictionary for 3rd party transitions
+ $.mobile.transitionHandlers = {
+ none: $.mobile.defaultTransitionHandler
+ };
+
+ //enable cross-domain page support
+ $.mobile.allowCrossDomainPages = false;
+
+ //return the original document url
+ $.mobile.getDocumentUrl = function(asParsedObject) {
+ return asParsedObject ? $.extend( {}, documentUrl ) : documentUrl.href;
+ };
+
+ //return the original document base url
+ $.mobile.getDocumentBase = function(asParsedObject) {
+ return asParsedObject ? $.extend( {}, documentBase ) : documentBase.href;
+ };
+
+ $.mobile._bindPageRemove = function() {
+ var page = $(this);
+
+ // when dom caching is not enabled or the page is embedded bind to remove the page on hide
+ if( !page.data("page").options.domCache
+ && page.is(":jqmData(external-page='true')") ) {
+
+ page.bind( 'pagehide.remove', function() {
+ var $this = $( this ),
+ prEvent = new $.Event( "pageremove" );
+
+ $this.trigger( prEvent );
+
+ if( !prEvent.isDefaultPrevented() ){
+ $this.removeWithDependents();
+ }
+ });
+ }
+ };
+
+ // Load a page into the DOM.
+ $.mobile.loadPage = function( url, options ) {
+ // This function uses deferred notifications to let callers
+ // know when the page is done loading, or if an error has occurred.
+ var deferred = $.Deferred(),
+
+ // The default loadPage options with overrides specified by
+ // the caller.
+ settings = $.extend( {}, $.mobile.loadPage.defaults, options ),
+
+ // The DOM element for the page after it has been loaded.
+ page = null,
+
+ // If the reloadPage option is true, and the page is already
+ // in the DOM, dupCachedPage will be set to the page element
+ // so that it can be removed after the new version of the
+ // page is loaded off the network.
+ dupCachedPage = null,
+
+ // determine the current base url
+ findBaseWithDefault = function(){
+ var closestBase = ( $.mobile.activePage && getClosestBaseUrl( $.mobile.activePage ) );
+ return closestBase || documentBase.hrefNoHash;
+ },
+
+ // The absolute version of the URL passed into the function. This
+ // version of the URL may contain dialog/subpage params in it.
+ absUrl = path.makeUrlAbsolute( url, findBaseWithDefault() );
+
+
+ // If the caller provided data, and we're using "get" request,
+ // append the data to the URL.
+ if ( settings.data && settings.type === "get" ) {
+ absUrl = path.addSearchParams( absUrl, settings.data );
+ settings.data = undefined;
+ }
+
+ // If the caller is using a "post" request, reloadPage must be true
+ if( settings.data && settings.type === "post" ){
+ settings.reloadPage = true;
+ }
+
+ // The absolute version of the URL minus any dialog/subpage params.
+ // In otherwords the real URL of the page to be loaded.
+ var fileUrl = path.getFilePath( absUrl ),
+
+ // The version of the Url actually stored in the data-url attribute of
+ // the page. For embedded pages, it is just the id of the page. For pages
+ // within the same domain as the document base, it is the site relative
+ // path. For cross-domain pages (Phone Gap only) the entire absolute Url
+ // used to load the page.
+ dataUrl = path.convertUrlToDataUrl( absUrl );
+
+ // Make sure we have a pageContainer to work with.
+ settings.pageContainer = settings.pageContainer || $.mobile.pageContainer;
+
+ // Check to see if the page already exists in the DOM.
+ page = settings.pageContainer.children( ":jqmData(url='" + dataUrl + "')" );
+
+ // If we failed to find the page, check to see if the url is a
+ // reference to an embedded page. If so, it may have been dynamically
+ // injected by a developer, in which case it would be lacking a data-url
+ // attribute and in need of enhancement.
+ if ( page.length === 0 && !path.isPath( dataUrl ) ) {
+ page = settings.pageContainer.children( "#" + dataUrl )
+ .attr( "data-" + $.mobile.ns + "url", dataUrl )
+ }
+
+ // If we failed to find a page in the DOM, check the URL to see if it
+ // refers to the first page in the application.
+ if ( page.length === 0 && $.mobile.firstPage && path.isFirstPageUrl( absUrl ) ) {
+ page = $( $.mobile.firstPage );
+ }
+
+ // Reset base to the default document base.
+ if ( base ) {
+ base.reset();
+ }
+
+ // If the page we are interested in is already in the DOM,
+ // and the caller did not indicate that we should force a
+ // reload of the file, we are done. Otherwise, track the
+ // existing page as a duplicated.
+ if ( page.length ) {
+ if ( !settings.reloadPage ) {
+ enhancePage( page, settings.role );
+ deferred.resolve( absUrl, options, page );
+ return deferred.promise();
+ }
+ dupCachedPage = page;
+ }
+
+ var mpc = settings.pageContainer,
+ pblEvent = new $.Event( "pagebeforeload" ),
+ triggerData = { url: url, absUrl: absUrl, dataUrl: dataUrl, deferred: deferred, options: settings };
+
+ // Let listeners know we're about to load a page.
+ mpc.trigger( pblEvent, triggerData );
+
+ // If the default behavior is prevented, stop here!
+ if( pblEvent.isDefaultPrevented() ){
+ return deferred.promise();
+ }
+
+ if ( settings.showLoadMsg ) {
+
+ // This configurable timeout allows cached pages a brief delay to load without showing a message
+ var loadMsgDelay = setTimeout(function(){
+ $.mobile.showPageLoadingMsg();
+ }, settings.loadMsgDelay ),
+
+ // Shared logic for clearing timeout and removing message.
+ hideMsg = function(){
+
+ // Stop message show timer
+ clearTimeout( loadMsgDelay );
+
+ // Hide loading message
+ $.mobile.hidePageLoadingMsg();
+ };
+ }
+
+ if ( !( $.mobile.allowCrossDomainPages || path.isSameDomain( documentUrl, absUrl ) ) ) {
+ deferred.reject( absUrl, options );
+ } else {
+ // Load the new page.
+ $.ajax({
+ url: fileUrl,
+ type: settings.type,
+ data: settings.data,
+ dataType: "html",
+ success: function( html ) {
+ //pre-parse html to check for a data-url,
+ //use it as the new fileUrl, base path, etc
+ var all = $( "<div></div>" ),
+
+ //page title regexp
+ newPageTitle = html.match( /<title[^>]*>([^<]*)/ ) && RegExp.$1,
+
+ // TODO handle dialogs again
+ pageElemRegex = new RegExp( "(<[^>]+\\bdata-" + $.mobile.ns + "role=[\"']?page[\"']?[^>]*>)" ),
+ dataUrlRegex = new RegExp( "\\bdata-" + $.mobile.ns + "url=[\"']?([^\"'>]*)[\"']?" );
+
+
+ // data-url must be provided for the base tag so resource requests can be directed to the
+ // correct url. loading into a temprorary element makes these requests immediately
+ if( pageElemRegex.test( html )
+ && RegExp.$1
+ && dataUrlRegex.test( RegExp.$1 )
+ && RegExp.$1 ) {
+ url = fileUrl = path.getFilePath( RegExp.$1 );
+ }
+
+ if ( base ) {
+ base.set( fileUrl );
+ }
+
+ //workaround to allow scripts to execute when included in page divs
+ all.get( 0 ).innerHTML = html;
+ page = all.find( ":jqmData(role='page'), :jqmData(role='dialog')" ).first();
+
+ //if page elem couldn't be found, create one and insert the body element's contents
+ if( !page.length ){
+ page = $( "<div data-" + $.mobile.ns + "role='page'>" + html.split( /<\/?body[^>]*>/gmi )[1] + "</div>" );
+ }
+
+ if ( newPageTitle && !page.jqmData( "title" ) ) {
+ page.jqmData( "title", newPageTitle );
+ }
+
+ //rewrite src and href attrs to use a base url
+ if( !$.support.dynamicBaseTag ) {
+ var newPath = path.get( fileUrl );
+ page.find( "[src], link[href], a[rel='external'], :jqmData(ajax='false'), a[target]" ).each(function() {
+ var thisAttr = $( this ).is( '[href]' ) ? 'href' :
+ $(this).is('[src]') ? 'src' : 'action',
+ thisUrl = $( this ).attr( thisAttr );
+
+ // XXX_jblas: We need to fix this so that it removes the document
+ // base URL, and then prepends with the new page URL.
+ //if full path exists and is same, chop it - helps IE out
+ thisUrl = thisUrl.replace( location.protocol + '//' + location.host + location.pathname, '' );
+
+ if( !/^(\w+:|#|\/)/.test( thisUrl ) ) {
+ $( this ).attr( thisAttr, newPath + thisUrl );
+ }
+ });
+ }
+
+ //append to page and enhance
+ // TODO taging a page with external to make sure that embedded pages aren't removed
+ // by the various page handling code is bad. Having page handling code in many
+ // places is bad. Solutions post 1.0
+ page
+ .attr( "data-" + $.mobile.ns + "url", path.convertUrlToDataUrl( fileUrl ) )
+ .attr( "data-" + $.mobile.ns + "external-page", true )
+ .appendTo( settings.pageContainer );
+
+ // wait for page creation to leverage options defined on widget
+ page.one( 'pagecreate', $.mobile._bindPageRemove );
+
+ enhancePage( page, settings.role );
+
+ // Enhancing the page may result in new dialogs/sub pages being inserted
+ // into the DOM. If the original absUrl refers to a sub-page, that is the
+ // real page we are interested in.
+ if ( absUrl.indexOf( "&" + $.mobile.subPageUrlKey ) > -1 ) {
+ page = settings.pageContainer.children( ":jqmData(url='" + dataUrl + "')" );
+ }
+
+ //bind pageHide to removePage after it's hidden, if the page options specify to do so
+
+ // Remove loading message.
+ if ( settings.showLoadMsg ) {
+ hideMsg();
+ }
+
+ // Add the page reference to our triggerData.
+ triggerData.page = page;
+
+ // Let listeners know the page loaded successfully.
+ settings.pageContainer.trigger( "pageload", triggerData );
+
+ deferred.resolve( absUrl, options, page, dupCachedPage );
+ },
+ error: function() {
+ //set base back to current path
+ if( base ) {
+ base.set( path.get() );
+ }
+
+ var plfEvent = new $.Event( "pageloadfailed" );
+
+ // Let listeners know the page load failed.
+ settings.pageContainer.trigger( plfEvent, triggerData );
+
+ // If the default behavior is prevented, stop here!
+ // Note that it is the responsibility of the listener/handler
+ // that called preventDefault(), to resolve/reject the
+ // deferred object within the triggerData.
+ if( plfEvent.isDefaultPrevented() ){
+ return;
+ }
+
+ // Remove loading message.
+ if ( settings.showLoadMsg ) {
+
+ // Remove loading message.
+ hideMsg();
+
+ //show error message
+ $( "<div class='ui-loader ui-overlay-shadow ui-body-e ui-corner-all'><h1>"+ $.mobile.pageLoadErrorMessage +"</h1></div>" )
+ .css({ "display": "block", "opacity": 0.96, "top": $window.scrollTop() + 100 })
+ .appendTo( settings.pageContainer )
+ .delay( 800 )
+ .fadeOut( 400, function() {
+ $( this ).remove();
+ });
+ }
+
+ deferred.reject( absUrl, options );
+ }
+ });
+ }
+
+ return deferred.promise();
+ };
+
+ $.mobile.loadPage.defaults = {
+ type: "get",
+ data: undefined,
+ reloadPage: false,
+ role: undefined, // By default we rely on the role defined by the @data-role attribute.
+ showLoadMsg: false,
+ pageContainer: undefined,
+ loadMsgDelay: 50 // This delay allows loads that pull from browser cache to occur without showing the loading message.
+ };
+
+ // Show a specific page in the page container.
+ $.mobile.changePage = function( toPage, options ) {
+ // If we are in the midst of a transition, queue the current request.
+ // We'll call changePage() once we're done with the current transition to
+ // service the request.
+ if( isPageTransitioning ) {
+ pageTransitionQueue.unshift( arguments );
+ return;
+ }
+
+ var settings = $.extend( {}, $.mobile.changePage.defaults, options );
+
+ // Make sure we have a pageContainer to work with.
+ settings.pageContainer = settings.pageContainer || $.mobile.pageContainer;
+
+ // Make sure we have a fromPage.
+ settings.fromPage = settings.fromPage || $.mobile.activePage;
+
+ var mpc = settings.pageContainer,
+ pbcEvent = new $.Event( "pagebeforechange" ),
+ triggerData = { toPage: toPage, options: settings };
+
+ // Let listeners know we're about to change the current page.
+ mpc.trigger( pbcEvent, triggerData );
+
+ // If the default behavior is prevented, stop here!
+ if( pbcEvent.isDefaultPrevented() ){
+ return;
+ }
+
+ // We allow "pagebeforechange" observers to modify the toPage in the trigger
+ // data to allow for redirects. Make sure our toPage is updated.
+
+ toPage = triggerData.toPage;
+
+ // Set the isPageTransitioning flag to prevent any requests from
+ // entering this method while we are in the midst of loading a page
+ // or transitioning.
+
+ isPageTransitioning = true;
+
+ // If the caller passed us a url, call loadPage()
+ // to make sure it is loaded into the DOM. We'll listen
+ // to the promise object it returns so we know when
+ // it is done loading or if an error ocurred.
+ if ( typeof toPage == "string" ) {
+ $.mobile.loadPage( toPage, settings )
+ .done(function( url, options, newPage, dupCachedPage ) {
+ isPageTransitioning = false;
+ options.duplicateCachedPage = dupCachedPage;
+ $.mobile.changePage( newPage, options );
+ })
+ .fail(function( url, options ) {
+ isPageTransitioning = false;
+
+ //clear out the active button state
+ removeActiveLinkClass( true );
+
+ //release transition lock so navigation is free again
+ releasePageTransitionLock();
+ settings.pageContainer.trigger( "pagechangefailed", triggerData );
+ });
+ return;
+ }
+
+ // The caller passed us a real page DOM element. Update our
+ // internal state and then trigger a transition to the page.
+ var fromPage = settings.fromPage,
+ url = ( settings.dataUrl && path.convertUrlToDataUrl( settings.dataUrl ) ) || toPage.jqmData( "url" ),
+ // The pageUrl var is usually the same as url, except when url is obscured as a dialog url. pageUrl always contains the file path
+ pageUrl = url,
+ fileUrl = path.getFilePath( url ),
+ active = urlHistory.getActive(),
+ activeIsInitialPage = urlHistory.activeIndex === 0,
+ historyDir = 0,
+ pageTitle = document.title,
+ isDialog = settings.role === "dialog" || toPage.jqmData( "role" ) === "dialog";
+
+ // By default, we prevent changePage requests when the fromPage and toPage
+ // are the same element, but folks that generate content manually/dynamically
+ // and reuse pages want to be able to transition to the same page. To allow
+ // this, they will need to change the default value of allowSamePageTransition
+ // to true, *OR*, pass it in as an option when they manually call changePage().
+ // It should be noted that our default transition animations assume that the
+ // formPage and toPage are different elements, so they may behave unexpectedly.
+ // It is up to the developer that turns on the allowSamePageTransitiona option
+ // to either turn off transition animations, or make sure that an appropriate
+ // animation transition is used.
+ if( fromPage && fromPage[0] === toPage[0] && !settings.allowSamePageTransition ) {
+ isPageTransitioning = false;
+ mpc.trigger( "pagechange", triggerData );
+ return;
+ }
+
+ // We need to make sure the page we are given has already been enhanced.
+ enhancePage( toPage, settings.role );
+
+ // If the changePage request was sent from a hashChange event, check to see if the
+ // page is already within the urlHistory stack. If so, we'll assume the user hit
+ // the forward/back button and will try to match the transition accordingly.
+ if( settings.fromHashChange ) {
+ urlHistory.directHashChange({
+ currentUrl: url,
+ isBack: function() { historyDir = -1; },
+ isForward: function() { historyDir = 1; }
+ });
+ }
+
+ // Kill the keyboard.
+ // XXX_jblas: We need to stop crawling the entire document to kill focus. Instead,
+ // we should be tracking focus with a live() handler so we already have
+ // the element in hand at this point.
+ // Wrap this in a try/catch block since IE9 throw "Unspecified error" if document.activeElement
+ // is undefined when we are in an IFrame.
+ try {
+ $( document.activeElement || "" ).add( "input:focus, textarea:focus, select:focus" ).blur();
+ } catch(e) {}
+
+ // If we're displaying the page as a dialog, we don't want the url
+ // for the dialog content to be used in the hash. Instead, we want
+ // to append the dialogHashKey to the url of the current page.
+ if ( isDialog && active ) {
+ // on the initial page load active.url is undefined and in that case should
+ // be an empty string. Moving the undefined -> empty string back into
+ // urlHistory.addNew seemed imprudent given undefined better represents
+ // the url state
+ url = ( active.url || "" ) + dialogHashKey;
+ }
+
+ // Set the location hash.
+ if( settings.changeHash !== false && url ) {
+ //disable hash listening temporarily
+ urlHistory.ignoreNextHashChange = true;
+ //update hash and history
+ path.set( url );
+ }
+
+ //if title element wasn't found, try the page div data attr too
+ var newPageTitle = toPage.jqmData( "title" ) || toPage.children(":jqmData(role='header')").find(".ui-title" ).text();
+ if( !!newPageTitle && pageTitle == document.title ) {
+ pageTitle = newPageTitle;
+ }
+
+ //add page to history stack if it's not back or forward
+ if( !historyDir ) {
+ urlHistory.addNew( url, settings.transition, pageTitle, pageUrl, settings.role );
+ }
+
+ //set page title
+ document.title = urlHistory.getActive().title;
+
+ //set "toPage" as activePage
+ $.mobile.activePage = toPage;
+
+ // Make sure we have a transition defined.
+ settings.transition = settings.transition
+ || ( ( historyDir && !activeIsInitialPage ) ? active.transition : undefined )
+ || ( isDialog ? $.mobile.defaultDialogTransition : $.mobile.defaultPageTransition );
+
+ // If we're navigating back in the URL history, set reverse accordingly.
+ settings.reverse = settings.reverse || historyDir < 0;
+
+ transitionPages( toPage, fromPage, settings.transition, settings.reverse )
+ .done(function() {
+ removeActiveLinkClass();
+
+ //if there's a duplicateCachedPage, remove it from the DOM now that it's hidden
+ if ( settings.duplicateCachedPage ) {
+ settings.duplicateCachedPage.remove();
+ }
+
+ //remove initial build class (only present on first pageshow)
+ $html.removeClass( "ui-mobile-rendering" );
+
+ releasePageTransitionLock();
+
+ // Let listeners know we're all done changing the current page.
+ mpc.trigger( "pagechange", triggerData );
+ });
+ };
+
+ $.mobile.changePage.defaults = {
+ transition: undefined,
+ reverse: false,
+ changeHash: true,
+ fromHashChange: false,
+ role: undefined, // By default we rely on the role defined by the @data-role attribute.
+ duplicateCachedPage: undefined,
+ pageContainer: undefined,
+ showLoadMsg: true, //loading message shows by default when pages are being fetched during changePage
+ dataUrl: undefined,
+ fromPage: undefined,
+ allowSamePageTransition: false
+ };
+
+/* Event Bindings - hashchange, submit, and click */
+ function findClosestLink( ele )
+ {
+ while ( ele ) {
+ if ( ele.nodeName.toLowerCase() == "a" ) {
+ break;
+ }
+ ele = ele.parentNode;
+ }
+ return ele;
+ }
+
+ // The base URL for any given element depends on the page it resides in.
+ function getClosestBaseUrl( ele )
+ {
+ // Find the closest page and extract out its url.
+ var url = $( ele ).closest( ".ui-page" ).jqmData( "url" ),
+ base = documentBase.hrefNoHash;
+
+ if ( !url || !path.isPath( url ) ) {
+ url = base;
+ }
+
+ return path.makeUrlAbsolute( url, base);
+ }
+
+
+ //The following event bindings should be bound after mobileinit has been triggered
+ //the following function is called in the init file
+ $.mobile._registerInternalEvents = function(){
+
+ //bind to form submit events, handle with Ajax
+ $( "form" ).live('submit', function( event ) {
+ var $this = $( this );
+ if( !$.mobile.ajaxEnabled ||
+ $this.is( ":jqmData(ajax='false')" ) ) {
+ return;
+ }
+
+ var type = $this.attr( "method" ),
+ target = $this.attr( "target" ),
+ url = $this.attr( "action" );
+
+ // If no action is specified, browsers default to using the
+ // URL of the document containing the form. Since we dynamically
+ // pull in pages from external documents, the form should submit
+ // to the URL for the source document of the page containing
+ // the form.
+ if ( !url ) {
+ // Get the @data-url for the page containing the form.
+ url = getClosestBaseUrl( $this );
+ if ( url === documentBase.hrefNoHash ) {
+ // The url we got back matches the document base,
+ // which means the page must be an internal/embedded page,
+ // so default to using the actual document url as a browser
+ // would.
+ url = documentUrl.hrefNoSearch;
+ }
+ }
+
+ url = path.makeUrlAbsolute( url, getClosestBaseUrl($this) );
+
+ //external submits use regular HTTP
+ if( path.isExternal( url ) || target ) {
+ return;
+ }
+
+ $.mobile.changePage(
+ url,
+ {
+ type: type && type.length && type.toLowerCase() || "get",
+ data: $this.serialize(),
+ transition: $this.jqmData( "transition" ),
+ direction: $this.jqmData( "direction" ),
+ reloadPage: true
+ }
+ );
+ event.preventDefault();
+ });
+
+ //add active state on vclick
+ $( document ).bind( "vclick", function( event ) {
+ // if this isn't a left click we don't care. Its important to note
+ // that when the virtual event is generated it will create
+ if ( event.which > 1 ){
+ return;
+ }
+
+ var link = findClosestLink( event.target );
+ if ( link ) {
+ if ( path.parseUrl( link.getAttribute( "href" ) || "#" ).hash !== "#" ) {
+ removeActiveLinkClass( true );
+ $activeClickedLink = $( link ).closest( ".ui-btn" ).not( ".ui-disabled" );
+ $activeClickedLink.addClass( $.mobile.activeBtnClass );
+ $( "." + $.mobile.activePageClass + " .ui-btn" ).not( link ).blur();
+ }
+ }
+ });
+
+ // click routing - direct to HTTP or Ajax, accordingly
+ $( document ).bind( "click", function( event ) {
+ var link = findClosestLink( event.target );
+
+ // If there is no link associated with the click or its not a left
+ // click we want to ignore the click
+ if ( !link || event.which > 1) {
+ return;
+ }
+
+ var $link = $( link ),
+ //remove active link class if external (then it won't be there if you come back)
+ httpCleanup = function(){
+ window.setTimeout( function() { removeActiveLinkClass( true ); }, 200 );
+ };
+
+ //if there's a data-rel=back attr, go back in history
+ if( $link.is( ":jqmData(rel='back')" ) ) {
+ window.history.back();
+ return false;
+ }
+
+ var baseUrl = getClosestBaseUrl( $link ),
+
+ //get href, if defined, otherwise default to empty hash
+ href = path.makeUrlAbsolute( $link.attr( "href" ) || "#", baseUrl );
+
+ //if ajax is disabled, exit early
+ if( !$.mobile.ajaxEnabled && !path.isEmbeddedPage( href ) ){
+ httpCleanup();
+ //use default click handling
+ return;
+ }
+
+ // XXX_jblas: Ideally links to application pages should be specified as
+ // an url to the application document with a hash that is either
+ // the site relative path or id to the page. But some of the
+ // internal code that dynamically generates sub-pages for nested
+ // lists and select dialogs, just write a hash in the link they
+ // create. This means the actual URL path is based on whatever
+ // the current value of the base tag is at the time this code
+ // is called. For now we are just assuming that any url with a
+ // hash in it is an application page reference.
+ if ( href.search( "#" ) != -1 ) {
+ href = href.replace( /[^#]*#/, "" );
+ if ( !href ) {
+ //link was an empty hash meant purely
+ //for interaction, so we ignore it.
+ event.preventDefault();
+ return;
+ } else if ( path.isPath( href ) ) {
+ //we have apath so make it the href we want to load.
+ href = path.makeUrlAbsolute( href, baseUrl );
+ } else {
+ //we have a simple id so use the documentUrl as its base.
+ href = path.makeUrlAbsolute( "#" + href, documentUrl.hrefNoHash );
+ }
+ }
+
+ // Should we handle this link, or let the browser deal with it?
+ var useDefaultUrlHandling = $link.is( "[rel='external']" ) || $link.is( ":jqmData(ajax='false')" ) || $link.is( "[target]" ),
+
+ // Some embedded browsers, like the web view in Phone Gap, allow cross-domain XHR
+ // requests if the document doing the request was loaded via the file:// protocol.
+ // This is usually to allow the application to "phone home" and fetch app specific
+ // data. We normally let the browser handle external/cross-domain urls, but if the
+ // allowCrossDomainPages option is true, we will allow cross-domain http/https
+ // requests to go through our page loading logic.
+ isCrossDomainPageLoad = ( $.mobile.allowCrossDomainPages && documentUrl.protocol === "file:" && href.search( /^https?:/ ) != -1 ),
+
+ //check for protocol or rel and its not an embedded page
+ //TODO overlap in logic from isExternal, rel=external check should be
+ // moved into more comprehensive isExternalLink
+ isExternal = useDefaultUrlHandling || ( path.isExternal( href ) && !isCrossDomainPageLoad );
+
+ if( isExternal ) {
+ httpCleanup();
+ //use default click handling
+ return;
+ }
+
+ //use ajax
+ var transition = $link.jqmData( "transition" ),
+ direction = $link.jqmData( "direction" ),
+ reverse = ( direction && direction === "reverse" ) ||
+ // deprecated - remove by 1.0
+ $link.jqmData( "back" ),
+
+ //this may need to be more specific as we use data-rel more
+ role = $link.attr( "data-" + $.mobile.ns + "rel" ) || undefined;
+
+ $.mobile.changePage( href, { transition: transition, reverse: reverse, role: role } );
+ event.preventDefault();
+ });
+
+ //prefetch pages when anchors with data-prefetch are encountered
+ $( ".ui-page" ).live( "pageshow.prefetch", function(){
+ var urls = [];
+ $( this ).find( "a:jqmData(prefetch)" ).each(function(){
+ var url = $( this ).attr( "href" );
+ if ( url && $.inArray( url, urls ) === -1 ) {
+ urls.push( url );
+ $.mobile.loadPage( url );
+ }
+ });
+ } );
+
+ $.mobile._handleHashChange = function( hash ) {
+ //find first page via hash
+ var to = path.stripHash( hash ),
+ //transition is false if it's the first page, undefined otherwise (and may be overridden by default)
+ transition = $.mobile.urlHistory.stack.length === 0 ? "none" : undefined,
+
+ // default options for the changPage calls made after examining the current state
+ // of the page and the hash
+ changePageOptions = {
+ transition: transition,
+ changeHash: false,
+ fromHashChange: true
+ };
+
+ //if listening is disabled (either globally or temporarily), or it's a dialog hash
+ if( !$.mobile.hashListeningEnabled || urlHistory.ignoreNextHashChange ) {
+ urlHistory.ignoreNextHashChange = false;
+ return;
+ }
+
+ // special case for dialogs
+ if( urlHistory.stack.length > 1 && to.indexOf( dialogHashKey ) > -1 ) {
+
+ // If current active page is not a dialog skip the dialog and continue
+ // in the same direction
+ if(!$.mobile.activePage.is( ".ui-dialog" )) {
+ //determine if we're heading forward or backward and continue accordingly past
+ //the current dialog
+ urlHistory.directHashChange({
+ currentUrl: to,
+ isBack: function() { window.history.back(); },
+ isForward: function() { window.history.forward(); }
+ });
+
+ // prevent changePage()
+ return;
+ } else {
+ // if the current active page is a dialog and we're navigating
+ // to a dialog use the dialog objected saved in the stack
+ urlHistory.directHashChange({
+ currentUrl: to,
+
+ // regardless of the direction of the history change
+ // do the following
+ either: function( isBack ) {
+ var active = $.mobile.urlHistory.getActive();
+
+ to = active.pageUrl;
+
+ // make sure to set the role, transition and reversal
+ // as most of this is lost by the domCache cleaning
+ $.extend( changePageOptions, {
+ role: active.role,
+ transition: active.transition,
+ reverse: isBack
+ });
+ }
+ });
+ }
+ }
+
+ //if to is defined, load it
+ if ( to ) {
+ // At this point, 'to' can be one of 3 things, a cached page element from
+ // a history stack entry, an id, or site-relative/absolute URL. If 'to' is
+ // an id, we need to resolve it against the documentBase, not the location.href,
+ // since the hashchange could've been the result of a forward/backward navigation
+ // that crosses from an external page/dialog to an internal page/dialog.
+ to = ( typeof to === "string" && !path.isPath( to ) ) ? ( path.makeUrlAbsolute( '#' + to, documentBase ) ) : to;
+ $.mobile.changePage( to, changePageOptions );
+ } else {
+ //there's no hash, go to the first page in the dom
+ $.mobile.changePage( $.mobile.firstPage, changePageOptions );
+ }
+ };
+
+ //hashchange event handler
+ $window.bind( "hashchange", function( e, triggered ) {
+ $.mobile._handleHashChange( location.hash );
+ });
+
+ //set page min-heights to be device specific
+ $( document ).bind( "pageshow", resetActivePageHeight );
+ $( window ).bind( "throttledresize", resetActivePageHeight );
+
+ };//_registerInternalEvents callback
+
+})( jQuery );
+/*
+* jQuery Mobile Framework : history.pushState support, layered on top of hashchange
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+( function( $, window ) {
+ // For now, let's Monkeypatch this onto the end of $.mobile._registerInternalEvents
+ // Scope self to pushStateHandler so we can reference it sanely within the
+ // methods handed off as event handlers
+ var pushStateHandler = {},
+ self = pushStateHandler,
+ $win = $( window ),
+ url = $.mobile.path.parseUrl( location.href );
+
+ $.extend( pushStateHandler, {
+ // TODO move to a path helper, this is rather common functionality
+ initialFilePath: (function() {
+ return url.pathname + url.search;
+ })(),
+
+ initialHref: url.hrefNoHash,
+
+ // Flag for tracking if a Hashchange naturally occurs after each popstate + replace
+ hashchangeFired: false,
+
+ state: function() {
+ return {
+ hash: location.hash || "#" + self.initialFilePath,
+ title: document.title,
+
+ // persist across refresh
+ initialHref: self.initialHref
+ };
+ },
+
+ resetUIKeys: function( url ) {
+ var dialog = $.mobile.dialogHashKey,
+ subkey = "&" + $.mobile.subPageUrlKey,
+ dialogIndex = url.indexOf( dialog );
+
+ if( dialogIndex > -1 ) {
+ url = url.slice( 0, dialogIndex ) + "#" + url.slice( dialogIndex );
+ } else if( url.indexOf( subkey ) > -1 ) {
+ url = url.split( subkey ).join( "#" + subkey );
+ }
+
+ return url;
+ },
+
+ // TODO sort out a single barrier to hashchange functionality
+ nextHashChangePrevented: function( value ) {
+ $.mobile.urlHistory.ignoreNextHashChange = value;
+ self.onHashChangeDisabled = value;
+ },
+
+ // on hash change we want to clean up the url
+ // NOTE this takes place *after* the vanilla navigation hash change
+ // handling has taken place and set the state of the DOM
+ onHashChange: function( e ) {
+ // disable this hash change
+ if( self.onHashChangeDisabled ){
+ return;
+ }
+
+ var href, state,
+ hash = location.hash,
+ isPath = $.mobile.path.isPath( hash );
+ hash = isPath ? hash.replace( "#", "" ) : hash;
+
+ // propulate the hash when its not available
+ state = self.state();
+
+ // make the hash abolute with the current href
+ href = $.mobile.path.makeUrlAbsolute( hash, location.href );
+
+ if ( isPath ) {
+ href = self.resetUIKeys( href );
+ }
+
+ // replace the current url with the new href and store the state
+ // Note that in some cases we might be replacing an url with the
+ // same url. We do this anyways because we need to make sure that
+ // all of our history entries have a state object associated with
+ // them. This allows us to work around the case where window.history.back()
+ // is called to transition from an external page to an embedded page.
+ // In that particular case, a hashchange event is *NOT* generated by the browser.
+ // Ensuring each history entry has a state object means that onPopState()
+ // will always trigger our hashchange callback even when a hashchange event
+ // is not fired.
+ history.replaceState( state, document.title, href );
+ },
+
+ // on popstate (ie back or forward) we need to replace the hash that was there previously
+ // cleaned up by the additional hash handling
+ onPopState: function( e ) {
+ var poppedState = e.originalEvent.state, holdnexthashchange = false;
+
+ // if there's no state its not a popstate we care about, ie chrome's initial popstate
+ // or forward popstate
+ if( poppedState ) {
+ // disable any hashchange triggered by the browser
+ self.nextHashChangePrevented( true );
+
+ // defer our manual hashchange until after the browser fired
+ // version has come and gone
+ setTimeout(function() {
+ // make sure that the manual hash handling takes place
+ self.nextHashChangePrevented( false );
+
+ // change the page based on the hash
+ $.mobile._handleHashChange( poppedState.hash );
+ }, 100);
+ }
+ },
+
+ init: function() {
+ $win.bind( "hashchange", self.onHashChange );
+
+ // Handle popstate events the occur through history changes
+ $win.bind( "popstate", self.onPopState );
+
+ // if there's no hash, we need to replacestate for returning to home
+ if ( location.hash === "" ) {
+ history.replaceState( self.state(), document.title, location.href );
+ }
+ }
+ });
+
+ $( function() {
+ if( $.mobile.pushStateEnabled && $.support.pushState ){
+ pushStateHandler.init();
+ }
+ });
+})( jQuery, this );/*!
+ * jQuery Mobile v@VERSION
+ * http://jquerymobile.com/
+ *
+ * Copyright 2010, jQuery Project
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ */
+
+(function( $, window, undefined ) {
+
+function css3TransitionHandler( name, reverse, $to, $from ) {
+
+ var deferred = new $.Deferred(),
+ reverseClass = reverse ? " reverse" : "",
+ viewportClass = "ui-mobile-viewport-transitioning viewport-" + name,
+ doneFunc = function() {
+
+ $to.add( $from ).removeClass( "out in reverse " + name );
+
+ if ( $from && $from[ 0 ] !== $to[ 0 ] ) {
+ $from.removeClass( $.mobile.activePageClass );
+ }
+
+ $to.parent().removeClass( viewportClass );
+
+ deferred.resolve( name, reverse, $to, $from );
+ };
+
+ $to.animationComplete( doneFunc );
+
+ $to.parent().addClass( viewportClass );
+
+ if ( $from ) {
+ $from.addClass( name + " out" + reverseClass );
+ }
+ $to.addClass( $.mobile.activePageClass + " " + name + " in" + reverseClass );
+
+ return deferred.promise();
+}
+
+// Make our transition handler public.
+$.mobile.css3TransitionHandler = css3TransitionHandler;
+
+// If the default transition handler is the 'none' handler, replace it with our handler.
+if ( $.mobile.defaultTransitionHandler === $.mobile.noneTransitionHandler ) {
+ $.mobile.defaultTransitionHandler = css3TransitionHandler;
+}
+
+})( jQuery, this );
+/*
+* jQuery Mobile Framework : "degradeInputs" plugin - degrades inputs to another type after custom enhancements are made.
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+
+(function( $, undefined ) {
+
+$.mobile.page.prototype.options.degradeInputs = {
+ color: false,
+ date: false,
+ datetime: false,
+ "datetime-local": false,
+ email: false,
+ month: false,
+ number: false,
+ range: "number",
+ search: "text",
+ tel: false,
+ time: false,
+ url: false,
+ week: false
+};
+
+$.mobile.page.prototype.options.keepNative = ":jqmData(role='none'), :jqmData(role='nojs')";
+
+
+//auto self-init widgets
+$( document ).bind( "pagecreate enhance", function( e ){
+
+ var page = $( e.target ).data( "page" ),
+ o = page.options;
+
+ // degrade inputs to avoid poorly implemented native functionality
+ $( e.target ).find( "input" ).not( o.keepNative ).each(function() {
+ var $this = $( this ),
+ type = this.getAttribute( "type" ),
+ optType = o.degradeInputs[ type ] || "text";
+
+ if ( o.degradeInputs[ type ] ) {
+ var html = $( "<div>" ).html( $this.clone() ).html(),
+ // In IE browsers, the type sometimes doesn't exist in the cloned markup, so we replace the closing tag instead
+ hasType = html.indexOf( " type=" ) > -1,
+ findstr = hasType ? /\s+type=["']?\w+['"]?/ : /\/?>/,
+ repstr = " type=\"" + optType + "\" data-" + $.mobile.ns + "type=\"" + type + "\"" + ( hasType ? "" : ">" );
+
+ $this.replaceWith( html.replace( findstr, repstr ) );
+ }
+ });
+
+});
+
+})( jQuery );/*
+* jQuery Mobile Framework : "dialog" plugin.
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+*/
+
+(function( $, window, undefined ) {
+
+$.widget( "mobile.dialog", $.mobile.widget, {
+ options: {
+ closeBtnText : "Close",
+ theme : "a",
+ initSelector : ":jqmData(role='dialog')"
+ },
+ _create: function() {
+ var self = this,
+ $el = this.element,
+ pageTheme = $el.attr( "class" ).match( /ui-body-[a-z]/ ),
+ headerCloseButton = $( "<a href='#' data-" + $.mobile.ns + "icon='delete' data-" + $.mobile.ns + "iconpos='notext'>"+ this.options.closeBtnText + "</a>" );
+
+ if( pageTheme.length ){
+ $el.removeClass( pageTheme[ 0 ] );
+ }
+
+ $el.addClass( "ui-body-" + this.options.theme );
+
+ // Class the markup for dialog styling
+ // Set aria role
+ $el.attr( "role", "dialog" )
+ .addClass( "ui-dialog" )
+ .find( ":jqmData(role='header')" )
+ .addClass( "ui-corner-top ui-overlay-shadow" )
+ .prepend( headerCloseButton )
+ .end()
+ .find( ":jqmData(role='content'),:jqmData(role='footer')" )
+ .last()
+ .addClass( "ui-corner-bottom ui-overlay-shadow" );
+
+ // this must be an anonymous function so that select menu dialogs can replace
+ // the close method. This is a change from previously just defining data-rel=back
+ // on the button and letting nav handle it
+ headerCloseButton.bind( "vclick", function() {
+ self.close();
+ });
+
+ /* bind events
+ - clicks and submits should use the closing transition that the dialog opened with
+ unless a data-transition is specified on the link/form
+ - if the click was on the close button, or the link has a data-rel="back" it'll go back in history naturally
+ */
+ $el.bind( "vclick submit", function( event ) {
+ var $target = $( event.target ).closest( event.type === "vclick" ? "a" : "form" ),
+ active;
+
+ if ( $target.length && !$target.jqmData( "transition" ) ) {
+
+ active = $.mobile.urlHistory.getActive() || {};
+
+ $target.attr( "data-" + $.mobile.ns + "transition", ( active.transition || $.mobile.defaultDialogTransition ) )
+ .attr( "data-" + $.mobile.ns + "direction", "reverse" );
+ }
+ })
+ .bind( "pagehide", function() {
+ $( this ).find( "." + $.mobile.activeBtnClass ).removeClass( $.mobile.activeBtnClass );
+ });
+ },
+
+ // Close method goes back in history
+ close: function() {
+ window.history.back();
+ }
+});
+
+//auto self-init widgets
+$( $.mobile.dialog.prototype.options.initSelector ).live( "pagecreate", function(){
+ $( this ).dialog();
+});
+
+})( jQuery, this );
+/*
+* jQuery Mobile Framework : This plugin handles theming and layout of headers, footers, and content areas
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+
+(function( $, undefined ) {
+
+$.mobile.page.prototype.options.backBtnText = "Back";
+$.mobile.page.prototype.options.addBackBtn = false;
+$.mobile.page.prototype.options.backBtnTheme = null;
+$.mobile.page.prototype.options.headerTheme = "a";
+$.mobile.page.prototype.options.footerTheme = "a";
+$.mobile.page.prototype.options.contentTheme = null;
+
+$( ":jqmData(role='page'), :jqmData(role='dialog')" ).live( "pagecreate", function( e ) {
+
+ var $page = $( this ),
+ o = $page.data( "page" ).options,
+ pageTheme = o.theme;
+
+ $( ":jqmData(role='header'), :jqmData(role='footer'), :jqmData(role='content')", this ).each(function() {
+ var $this = $( this ),
+ role = $this.jqmData( "role" ),
+ theme = $this.jqmData( "theme" ),
+ $headeranchors,
+ leftbtn,
+ rightbtn,
+ backBtn;
+
+ $this.addClass( "ui-" + role );
+
+ //apply theming and markup modifications to page,header,content,footer
+ if ( role === "header" || role === "footer" ) {
+
+ var thisTheme = theme || ( role === "header" ? o.headerTheme : o.footerTheme ) || pageTheme;
+
+ //add theme class
+ $this.addClass( "ui-bar-" + thisTheme );
+
+ // Add ARIA role
+ $this.attr( "role", role === "header" ? "banner" : "contentinfo" );
+
+ // Right,left buttons
+ $headeranchors = $this.children( "a" );
+ leftbtn = $headeranchors.hasClass( "ui-btn-left" );
+ rightbtn = $headeranchors.hasClass( "ui-btn-right" );
+
+ if ( !leftbtn ) {
+ leftbtn = $headeranchors.eq( 0 ).not( ".ui-btn-right" ).addClass( "ui-btn-left" ).length;
+ }
+
+ if ( !rightbtn ) {
+ rightbtn = $headeranchors.eq( 1 ).addClass( "ui-btn-right" ).length;
+ }
+
+ // Auto-add back btn on pages beyond first view
+ if ( o.addBackBtn && role === "header" &&
+ $( ".ui-page" ).length > 1 &&
+ $this.jqmData( "url" ) !== $.mobile.path.stripHash( location.hash ) &&
+ !leftbtn ) {
+
+ backBtn = $( "<a href='#' class='ui-btn-left' data-"+ $.mobile.ns +"rel='back' data-"+ $.mobile.ns +"icon='arrow-l'>"+ o.backBtnText +"</a>" ).prependTo( $this );
+
+ // If theme is provided, override default inheritance
+ backBtn.attr( "data-"+ $.mobile.ns +"theme", o.backBtnTheme || thisTheme );
+ }
+
+ // Page title
+ $this.children( "h1, h2, h3, h4, h5, h6" )
+ .addClass( "ui-title" )
+ // Regardless of h element number in src, it becomes h1 for the enhanced page
+ .attr({
+ "tabindex": "0",
+ "role": "heading",
+ "aria-level": "1"
+ });
+
+ } else if ( role === "content" ) {
+
+ if (theme || o.contentTheme) {
+ $this.addClass( "ui-body-" + ( theme || o.contentTheme ) );
+ }
+
+ // Add ARIA role
+ $this.attr( "role", "main" );
+
+ }
+ });
+});
+
+})( jQuery );/*
+* jQuery Mobile Framework : "collapsible" plugin
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+(function( $, undefined ) {
+
+$.widget( "mobile.collapsible", $.mobile.widget, {
+ options: {
+ expandCueText: " click to expand contents",
+ collapseCueText: " click to collapse contents",
+ collapsed: true,
+ heading: ">:header,>legend",
+ theme: null,
+ contentTheme: null,
+ iconTheme: "d",
+ initSelector: ":jqmData(role='collapsible')"
+ },
+ _create: function() {
+
+ var $el = this.element,
+ o = this.options,
+ collapsible = $el.addClass( "ui-collapsible" ),
+ collapsibleHeading = $el.find( o.heading ).eq( 0 ),
+ collapsibleContent = collapsible.wrapInner( "<div class='ui-collapsible-content'></div>" ).find( ".ui-collapsible-content" ),
+ collapsibleSet = $el.closest( ":jqmData(role='collapsible-set')" ).addClass( "ui-collapsible-set" ),
+ colllapsiblesInSet = collapsibleSet.children( ":jqmData(role='collapsible')" );
+
+ // Replace collapsibleHeading if it's a legend
+ if ( collapsibleHeading.is( "legend" ) ) {
+ collapsibleHeading = $( "<div role='heading'>"+ collapsibleHeading.html() +"</div>" ).insertBefore( collapsibleHeading );
+ collapsibleHeading.next().remove();
+ }
+
+ // If we are in a collapsible set
+ if ( collapsibleSet.length ) {
+ // Inherit the theme from collapsible-set
+ if ( !o.theme ) {
+ o.theme = collapsibleSet.jqmData( "theme" );
+ }
+ // Inherit the content-theme from collapsible-set
+ if ( !o.contentTheme ) {
+ o.contentTheme = collapsibleSet.jqmData( "content-theme" );
+ }
+ }
+
+ collapsibleContent.addClass( ( o.contentTheme ) ? ( "ui-body-" + o.contentTheme ) : "");
+
+ collapsibleHeading
+ //drop heading in before content
+ .insertBefore( collapsibleContent )
+ //modify markup & attributes
+ .addClass( "ui-collapsible-heading" )
+ .append( "<span class='ui-collapsible-heading-status'></span>" )
+ .wrapInner( "<a href='#' class='ui-collapsible-heading-toggle'></a>" )
+ .find( "a:eq(0)" )
+ .buttonMarkup({
+ shadow: false,
+ corners: false,
+ iconPos: "left",
+ icon: "plus",
+ theme: o.theme
+ });
+
+ if ( !collapsibleSet.length ) {
+ collapsibleHeading
+ .find( "a:eq(0), .ui-btn-inner" )
+ .addClass( "ui-corner-top ui-corner-bottom" );
+ } else {
+ // If we are in a collapsible set
+
+ // Initialize the collapsible set if it's not already initialized
+ if ( !collapsibleSet.jqmData( "collapsiblebound" ) ) {
+
+ collapsibleSet
+ .jqmData( "collapsiblebound", true )
+ .bind( "expand", function( event ) {
+
+ $( event.target )
+ .closest( ".ui-collapsible" )
+ .siblings( ".ui-collapsible" )
+ .trigger( "collapse" );
+
+ });
+ }
+
+ colllapsiblesInSet.first()
+ .find( "a:eq(0)" )
+ .addClass( "ui-corner-top" )
+ .find( ".ui-btn-inner" )
+ .addClass( "ui-corner-top" );
+
+ colllapsiblesInSet.last()
+ .jqmData( "collapsible-last", true )
+ .find( "a:eq(0)" )
+ .addClass( "ui-corner-bottom" )
+ .find( ".ui-btn-inner" )
+ .addClass( "ui-corner-bottom" );
+
+
+ if ( collapsible.jqmData( "collapsible-last" ) ) {
+ collapsibleHeading
+ .find( "a:eq(0), .ui-btn-inner" )
+ .addClass( "ui-corner-bottom" );
+ }
+ }
+
+ //events
+ collapsible
+ .bind( "expand collapse", function( event ) {
+ if ( !event.isDefaultPrevented() ) {
+
+ event.preventDefault();
+
+ var $this = $( this ),
+ isCollapse = ( event.type === "collapse" ),
+ contentTheme = o.contentTheme;
+
+ collapsibleHeading
+ .toggleClass( "ui-collapsible-heading-collapsed", isCollapse)
+ .find( ".ui-collapsible-heading-status" )
+ .text( o.expandCueText )
+ .end()
+ .find( ".ui-icon" )
+ .toggleClass( "ui-icon-minus", !isCollapse )
+ .toggleClass( "ui-icon-plus", isCollapse );
+
+ $this.toggleClass( "ui-collapsible-collapsed", isCollapse );
+ collapsibleContent.toggleClass( "ui-collapsible-content-collapsed", isCollapse ).attr( "aria-hidden", isCollapse );
+
+ if ( contentTheme && ( !collapsibleSet.length || collapsible.jqmData( "collapsible-last" ) ) ) {
+ collapsibleHeading
+ .find( "a:eq(0), .ui-btn-inner" )
+ .toggleClass( "ui-corner-bottom", isCollapse );
+ collapsibleContent.toggleClass( "ui-corner-bottom", !isCollapse );
+ }
+ }
+ })
+ .trigger( o.collapsed ? "collapse" : "expand" );
+
+ collapsibleHeading
+ .bind( "click", function( event ) {
+
+ var type = collapsibleHeading.is( ".ui-collapsible-heading-collapsed" ) ?
+ "expand" : "collapse";
+
+ collapsible.trigger( type );
+
+ event.preventDefault();
+ });
+ }
+});
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+ $( $.mobile.collapsible.prototype.options.initSelector, e.target ).collapsible();
+});
+
+})( jQuery );
+/*
+* jQuery Mobile Framework : "fieldcontain" plugin - simple class additions to make form row separators
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+
+(function( $, undefined ) {
+
+$.fn.fieldcontain = function( options ) {
+ return this.addClass( "ui-field-contain ui-body ui-br" );
+};
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+ $( ":jqmData(role='fieldcontain')", e.target ).fieldcontain();
+});
+
+})( jQuery );/*
+* jQuery Mobile Framework : plugin for creating CSS grids
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+
+(function( $, undefined ) {
+
+$.fn.grid = function( options ) {
+ return this.each(function() {
+
+ var $this = $( this ),
+ o = $.extend({
+ grid: null
+ },options),
+ $kids = $this.children(),
+ gridCols = {solo:1, a:2, b:3, c:4, d:5},
+ grid = o.grid,
+ iterator;
+
+ if ( !grid ) {
+ if ( $kids.length <= 5 ) {
+ for ( var letter in gridCols ) {
+ if ( gridCols[ letter ] === $kids.length ) {
+ grid = letter;
+ }
+ }
+ } else {
+ grid = "a";
+ }
+ }
+ iterator = gridCols[grid];
+
+ $this.addClass( "ui-grid-" + grid );
+
+ $kids.filter( ":nth-child(" + iterator + "n+1)" ).addClass( "ui-block-a" );
+
+ if ( iterator > 1 ) {
+ $kids.filter( ":nth-child(" + iterator + "n+2)" ).addClass( "ui-block-b" );
+ }
+ if ( iterator > 2 ) {
+ $kids.filter( ":nth-child(3n+3)" ).addClass( "ui-block-c" );
+ }
+ if ( iterator > 3 ) {
+ $kids.filter( ":nth-child(4n+4)" ).addClass( "ui-block-d" );
+ }
+ if ( iterator > 4 ) {
+ $kids.filter( ":nth-child(5n+5)" ).addClass( "ui-block-e" );
+ }
+ });
+};
+})( jQuery );/*
+* jQuery Mobile Framework : "navbar" plugin
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+
+(function( $, undefined ) {
+
+$.widget( "mobile.navbar", $.mobile.widget, {
+ options: {
+ iconpos: "top",
+ grid: null,
+ initSelector: ":jqmData(role='navbar')"
+ },
+
+ _create: function(){
+
+ var $navbar = this.element,
+ $navbtns = $navbar.find( "a" ),
+ iconpos = $navbtns.filter( ":jqmData(icon)" ).length ?
+ this.options.iconpos : undefined;
+
+ $navbar.addClass( "ui-navbar" )
+ .attr( "role","navigation" )
+ .find( "ul" )
+ .grid({ grid: this.options.grid });
+
+ if ( !iconpos ) {
+ $navbar.addClass( "ui-navbar-noicons" );
+ }
+
+ $navbtns.buttonMarkup({
+ corners: false,
+ shadow: false,
+ iconpos: iconpos
+ });
+
+ $navbar.delegate( "a", "vclick", function( event ) {
+ $navbtns.not( ".ui-state-persist" ).removeClass( $.mobile.activeBtnClass );
+ $( this ).addClass( $.mobile.activeBtnClass );
+ });
+ }
+});
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+ $( $.mobile.navbar.prototype.options.initSelector, e.target ).navbar();
+});
+
+})( jQuery );
+/*
+* jQuery Mobile Framework : "listview" plugin
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+
+(function( $, undefined ) {
+
+//Keeps track of the number of lists per page UID
+//This allows support for multiple nested list in the same page
+//https://github.com/jquery/jquery-mobile/issues/1617
+var listCountPerPage = {};
+
+$.widget( "mobile.listview", $.mobile.widget, {
+ options: {
+ theme: "c",
+ countTheme: "c",
+ headerTheme: "b",
+ dividerTheme: "b",
+ splitIcon: "arrow-r",
+ splitTheme: "b",
+ inset: false,
+ initSelector: ":jqmData(role='listview')"
+ },
+
+ _create: function() {
+ var t = this;
+
+ // create listview markup
+ t.element.addClass(function( i, orig ) {
+ return orig + " ui-listview " + ( t.options.inset ? " ui-listview-inset ui-corner-all ui-shadow " : "" );
+ });
+
+ t.refresh( true );
+ },
+
+ _itemApply: function( $list, item ) {
+ var $countli = item.find( ".ui-li-count" );
+ if ( $countli.length ) {
+ item.addClass( "ui-li-has-count" );
+ }
+ $countli.addClass( "ui-btn-up-" + ( $list.jqmData( "counttheme" ) || this.options.countTheme ) + " ui-btn-corner-all" );
+
+ // TODO class has to be defined in markup
+ item.find( "h1, h2, h3, h4, h5, h6" ).addClass( "ui-li-heading" ).end()
+ .find( "p, dl" ).addClass( "ui-li-desc" ).end()
+ .find( ">img:eq(0), .ui-link-inherit>img:eq(0)" ).addClass( "ui-li-thumb" ).each(function() {
+ item.addClass( $(this).is( ".ui-li-icon" ) ? "ui-li-has-icon" : "ui-li-has-thumb" );
+ }).end()
+ .find( ".ui-li-aside" ).each(function() {
+ var $this = $(this);
+ $this.prependTo( $this.parent() ); //shift aside to front for css float
+ });
+ },
+
+ _removeCorners: function( li, which ) {
+ var top = "ui-corner-top ui-corner-tr ui-corner-tl",
+ bot = "ui-corner-bottom ui-corner-br ui-corner-bl";
+
+ li = li.add( li.find( ".ui-btn-inner, .ui-li-link-alt, .ui-li-thumb" ) );
+
+ if ( which === "top" ) {
+ li.removeClass( top );
+ } else if ( which === "bottom" ) {
+ li.removeClass( bot );
+ } else {
+ li.removeClass( top + " " + bot );
+ }
+ },
+
+ _refreshCorners: function( create ) {
+ var $li,
+ $visibleli,
+ $topli,
+ $bottomli;
+
+ if ( this.options.inset ) {
+ $li = this.element.children( "li" );
+ // at create time the li are not visible yet so we need to rely on .ui-screen-hidden
+ $visibleli = create?$li.not( ".ui-screen-hidden" ):$li.filter( ":visible" );
+
+ this._removeCorners( $li );
+
+ // Select the first visible li element
+ $topli = $visibleli.first()
+ .addClass( "ui-corner-top" );
+
+ $topli.add( $topli.find( ".ui-btn-inner" ) )
+ .find( ".ui-li-link-alt" )
+ .addClass( "ui-corner-tr" )
+ .end()
+ .find( ".ui-li-thumb" )
+ .addClass( "ui-corner-tl" );
+
+ // Select the last visible li element
+ $bottomli = $visibleli.last()
+ .addClass( "ui-corner-bottom" );
+
+ $bottomli.add( $bottomli.find( ".ui-btn-inner" ) )
+ .find( ".ui-li-link-alt" )
+ .addClass( "ui-corner-br" )
+ .end()
+ .find( ".ui-li-thumb" )
+ .addClass( "ui-corner-bl" );
+ }
+ },
+
+ refresh: function( create ) {
+ this.parentPage = this.element.closest( ".ui-page" );
+ this._createSubPages();
+
+ var o = this.options,
+ $list = this.element,
+ self = this,
+ dividertheme = $list.jqmData( "dividertheme" ) || o.dividerTheme,
+ listsplittheme = $list.jqmData( "splittheme" ),
+ listspliticon = $list.jqmData( "spliticon" ),
+ li = $list.children( "li" ),
+ counter = $.support.cssPseudoElement || !$.nodeName( $list[ 0 ], "ol" ) ? 0 : 1,
+ item, itemClass, itemTheme,
+ a, last, splittheme, countParent, icon;
+
+ if ( counter ) {
+ $list.find( ".ui-li-dec" ).remove();
+ }
+
+ for ( var pos = 0, numli = li.length; pos < numli; pos++ ) {
+ item = li.eq( pos );
+ itemClass = "ui-li";
+
+ // If we're creating the element, we update it regardless
+ if ( create || !item.hasClass( "ui-li" ) ) {
+ itemTheme = item.jqmData("theme") || o.theme;
+ a = item.children( "a" );
+
+ if ( a.length ) {
+ icon = item.jqmData("icon");
+
+ item.buttonMarkup({
+ wrapperEls: "div",
+ shadow: false,
+ corners: false,
+ iconpos: "right",
+ icon: a.length > 1 || icon === false ? false : icon || "arrow-r",
+ theme: itemTheme
+ });
+
+ if ( ( icon != false ) && ( a.length == 1 ) ) {
+ item.addClass( "ui-li-has-arrow" );
+ }
+
+ a.first().addClass( "ui-link-inherit" );
+
+ if ( a.length > 1 ) {
+ itemClass += " ui-li-has-alt";
+
+ last = a.last();
+ splittheme = listsplittheme || last.jqmData( "theme" ) || o.splitTheme;
+
+ last.appendTo(item)
+ .attr( "title", last.text() )
+ .addClass( "ui-li-link-alt" )
+ .empty()
+ .buttonMarkup({
+ shadow: false,
+ corners: false,
+ theme: itemTheme,
+ icon: false,
+ iconpos: false
+ })
+ .find( ".ui-btn-inner" )
+ .append(
+ $( "<span />" ).buttonMarkup({
+ shadow: true,
+ corners: true,
+ theme: splittheme,
+ iconpos: "notext",
+ icon: listspliticon || last.jqmData( "icon" ) || o.splitIcon
+ })
+ );
+ }
+ } else if ( item.jqmData( "role" ) === "list-divider" ) {
+
+ itemClass += " ui-li-divider ui-btn ui-bar-" + dividertheme;
+ item.attr( "role", "heading" );
+
+ //reset counter when a divider heading is encountered
+ if ( counter ) {
+ counter = 1;
+ }
+
+ } else {
+ itemClass += " ui-li-static ui-body-" + itemTheme;
+ }
+ }
+
+ if ( counter && itemClass.indexOf( "ui-li-divider" ) < 0 ) {
+ countParent = item.is( ".ui-li-static:first" ) ? item : item.find( ".ui-link-inherit" );
+
+ countParent.addClass( "ui-li-jsnumbering" )
+ .prepend( "<span class='ui-li-dec'>" + (counter++) + ". </span>" );
+ }
+
+ item.add( item.children( ".ui-btn-inner" ) ).addClass( itemClass );
+
+ self._itemApply( $list, item );
+ }
+
+ this._refreshCorners( create );
+ },
+
+ //create a string for ID/subpage url creation
+ _idStringEscape: function( str ) {
+ return str.replace(/[^a-zA-Z0-9]/g, '-');
+ },
+
+ _createSubPages: function() {
+ var parentList = this.element,
+ parentPage = parentList.closest( ".ui-page" ),
+ parentUrl = parentPage.jqmData( "url" ),
+ parentId = parentUrl || parentPage[ 0 ][ $.expando ],
+ parentListId = parentList.attr( "id" ),
+ o = this.options,
+ dns = "data-" + $.mobile.ns,
+ self = this,
+ persistentFooterID = parentPage.find( ":jqmData(role='footer')" ).jqmData( "id" ),
+ hasSubPages;
+
+ if ( typeof listCountPerPage[ parentId ] === "undefined" ) {
+ listCountPerPage[ parentId ] = -1;
+ }
+
+ parentListId = parentListId || ++listCountPerPage[ parentId ];
+
+ $( parentList.find( "li>ul, li>ol" ).toArray().reverse() ).each(function( i ) {
+ var self = this,
+ list = $( this ),
+ listId = list.attr( "id" ) || parentListId + "-" + i,
+ parent = list.parent(),
+ nodeEls = $( list.prevAll().toArray().reverse() ),
+ nodeEls = nodeEls.length ? nodeEls : $( "<span>" + $.trim(parent.contents()[ 0 ].nodeValue) + "</span>" ),
+ title = nodeEls.first().text(),//url limits to first 30 chars of text
+ id = ( parentUrl || "" ) + "&" + $.mobile.subPageUrlKey + "=" + listId,
+ theme = list.jqmData( "theme" ) || o.theme,
+ countTheme = list.jqmData( "counttheme" ) || parentList.jqmData( "counttheme" ) || o.countTheme,
+ newPage, anchor;
+
+ //define hasSubPages for use in later removal
+ hasSubPages = true;
+
+ newPage = list.detach()
+ .wrap( "<div " + dns + "role='page' " + dns + "url='" + id + "' " + dns + "theme='" + theme + "' " + dns + "count-theme='" + countTheme + "'><div " + dns + "role='content'></div></div>" )
+ .parent()
+ .before( "<div " + dns + "role='header' " + dns + "theme='" + o.headerTheme + "'><div class='ui-title'>" + title + "</div></div>" )
+ .after( persistentFooterID ? $( "<div " + dns + "role='footer' " + dns + "id='"+ persistentFooterID +"'>") : "" )
+ .parent()
+ .appendTo( $.mobile.pageContainer );
+
+ newPage.page();
+
+ anchor = parent.find('a:first');
+
+ if ( !anchor.length ) {
+ anchor = $( "<a/>" ).html( nodeEls || title ).prependTo( parent.empty() );
+ }
+
+ anchor.attr( "href", "#" + id );
+
+ }).listview();
+
+ // on pagehide, remove any nested pages along with the parent page, as long as they aren't active
+ // and aren't embedded
+ if( hasSubPages &&
+ parentPage.is( ":jqmData(external-page='true')" ) &&
+ parentPage.data("page").options.domCache === false ) {
+
+ var newRemove = function( e, ui ){
+ var nextPage = ui.nextPage, npURL;
+
+ if( ui.nextPage ){
+ npURL = nextPage.jqmData( "url" );
+ if( npURL.indexOf( parentUrl + "&" + $.mobile.subPageUrlKey ) !== 0 ){
+ self.childPages().remove();
+ parentPage.remove();
+ }
+ }
+ };
+
+ // unbind the original page remove and replace with our specialized version
+ parentPage
+ .unbind( "pagehide.remove" )
+ .bind( "pagehide.remove", newRemove);
+ }
+ },
+
+ // TODO sort out a better way to track sub pages of the listview this is brittle
+ childPages: function(){
+ var parentUrl = this.parentPage.jqmData( "url" );
+
+ return $( ":jqmData(url^='"+ parentUrl + "&" + $.mobile.subPageUrlKey +"')");
+ }
+});
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+ $( $.mobile.listview.prototype.options.initSelector, e.target ).listview();
+});
+
+})( jQuery );
+/*
+* jQuery Mobile Framework : "listview" filter extension
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+
+(function( $, undefined ) {
+
+$.mobile.listview.prototype.options.filter = false;
+$.mobile.listview.prototype.options.filterPlaceholder = "Filter items...";
+$.mobile.listview.prototype.options.filterTheme = "c";
+$.mobile.listview.prototype.options.filterCallback = function( text, searchValue ){
+ return text.toLowerCase().indexOf( searchValue ) === -1;
+};
+
+$( ":jqmData(role='listview')" ).live( "listviewcreate", function() {
+
+ var list = $( this ),
+ listview = list.data( "listview" );
+
+ if ( !listview.options.filter ) {
+ return;
+ }
+
+ var wrapper = $( "<form>", {
+ "class": "ui-listview-filter ui-bar-" + listview.options.filterTheme,
+ "role": "search"
+ }),
+ search = $( "<input>", {
+ placeholder: listview.options.filterPlaceholder
+ })
+ .attr( "data-" + $.mobile.ns + "type", "search" )
+ .jqmData( "lastval", "" )
+ .bind( "keyup change", function() {
+
+ var $this = $(this),
+ val = this.value.toLowerCase(),
+ listItems = null,
+ lastval = $this.jqmData( "lastval" ) + "",
+ childItems = false,
+ itemtext = "",
+ item, change;
+
+ // Change val as lastval for next execution
+ $this.jqmData( "lastval" , val );
+
+ change = val.replace( new RegExp( "^" + lastval ) , "" );
+
+ if ( val.length < lastval.length || change.length != ( val.length - lastval.length ) ) {
+
+ // Removed chars or pasted something totaly different, check all items
+ listItems = list.children();
+ } else {
+
+ // Only chars added, not removed, only use visible subset
+ listItems = list.children( ":not(.ui-screen-hidden)" );
+ }
+
+ if ( val ) {
+
+ // This handles hiding regular rows without the text we search for
+ // and any list dividers without regular rows shown under it
+
+ for ( var i = listItems.length - 1; i >= 0; i-- ) {
+ item = $( listItems[ i ] );
+ itemtext = item.jqmData( "filtertext" ) || item.text();
+
+ if ( item.is( "li:jqmData(role=list-divider)" ) ) {
+
+ item.toggleClass( "ui-filter-hidequeue" , !childItems );
+
+ // New bucket!
+ childItems = false;
+
+ } else if ( listview.options.filterCallback( itemtext, val ) ) {
+
+ //mark to be hidden
+ item.toggleClass( "ui-filter-hidequeue" , true );
+ } else {
+
+ // There"s a shown item in the bucket
+ childItems = true;
+ }
+ }
+
+ // Show items, not marked to be hidden
+ listItems
+ .filter( ":not(.ui-filter-hidequeue)" )
+ .toggleClass( "ui-screen-hidden", false );
+
+ // Hide items, marked to be hidden
+ listItems
+ .filter( ".ui-filter-hidequeue" )
+ .toggleClass( "ui-screen-hidden", true )
+ .toggleClass( "ui-filter-hidequeue", false );
+
+ } else {
+
+ //filtervalue is empty => show all
+ listItems.toggleClass( "ui-screen-hidden", false );
+ }
+ listview._refreshCorners();
+ })
+ .appendTo( wrapper )
+ .textinput();
+
+ if ( $( this ).jqmData( "inset" ) ) {
+ wrapper.addClass( "ui-listview-filter-inset" );
+ }
+
+ wrapper.bind( "submit", function() {
+ return false;
+ })
+ .insertBefore( list );
+});
+
+})( jQuery );/*
+* jQuery Mobile Framework : "nojs" plugin - class to make elements hidden to A grade browsers
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+
+(function( $, undefined ) {
+
+$( document ).bind( "pagecreate create", function( e ){
+ $( ":jqmData(role='nojs')", e.target ).addClass( "ui-nojs" );
+
+});
+
+})( jQuery );/*
+* jQuery Mobile Framework : "checkboxradio" plugin
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+
+(function( $, undefined ) {
+
+$.widget( "mobile.checkboxradio", $.mobile.widget, {
+ options: {
+ theme: null,
+ initSelector: "input[type='checkbox'],input[type='radio']"
+ },
+ _create: function() {
+ var self = this,
+ input = this.element,
+ // NOTE: Windows Phone could not find the label through a selector
+ // filter works though.
+ label = input.closest( "form,fieldset,:jqmData(role='page')" ).find( "label" ).filter( "[for='" + input[ 0 ].id + "']"),
+ inputtype = input.attr( "type" ),
+ checkedState = inputtype + "-on",
+ uncheckedState = inputtype + "-off",
+ icon = input.parents( ":jqmData(type='horizontal')" ).length ? undefined : uncheckedState,
+ activeBtn = icon ? "" : " " + $.mobile.activeBtnClass,
+ checkedClass = "ui-" + checkedState + activeBtn,
+ uncheckedClass = "ui-" + uncheckedState,
+ checkedicon = "ui-icon-" + checkedState,
+ uncheckedicon = "ui-icon-" + uncheckedState;
+
+ if ( inputtype !== "checkbox" && inputtype !== "radio" ) {
+ return;
+ }
+
+ // Expose for other methods
+ $.extend( this, {
+ label: label,
+ inputtype: inputtype,
+ checkedClass: checkedClass,
+ uncheckedClass: uncheckedClass,
+ checkedicon: checkedicon,
+ uncheckedicon: uncheckedicon
+ });
+
+ // If there's no selected theme...
+ if( !this.options.theme ) {
+ this.options.theme = this.element.jqmData( "theme" );
+ }
+
+ label.buttonMarkup({
+ theme: this.options.theme,
+ icon: icon,
+ shadow: false
+ });
+
+ // Wrap the input + label in a div
+ input.add( label )
+ .wrapAll( "<div class='ui-" + inputtype + "'></div>" );
+
+ label.bind({
+ vmouseover: function() {
+ if ( $( this ).parent().is( ".ui-disabled" ) ) {
+ return false;
+ }
+ },
+
+ vclick: function( event ) {
+ if ( input.is( ":disabled" ) ) {
+ event.preventDefault();
+ return;
+ }
+
+ self._cacheVals();
+
+ input.prop( "checked", inputtype === "radio" && true || !input.prop( "checked" ) );
+
+ // Input set for common radio buttons will contain all the radio
+ // buttons, but will not for checkboxes. clearing the checked status
+ // of other radios ensures the active button state is applied properly
+ self._getInputSet().not( input ).prop( "checked", false );
+
+ self._updateAll();
+ return false;
+ }
+
+ });
+
+ input
+ .bind({
+ vmousedown: function() {
+ this._cacheVals();
+ },
+
+ vclick: function() {
+
+ var $this = $(this);
+
+ // Adds checked attribute to checked input when keyboard is used
+ if ( $this.is( ":checked" ) ) {
+
+ $this.prop( "checked", true);
+ self._getInputSet().not($this).prop( "checked", false );
+ } else {
+
+ $this.prop( "checked", false );
+ }
+
+ self._updateAll();
+ },
+
+ focus: function() {
+ label.addClass( "ui-focus" );
+ },
+
+ blur: function() {
+ label.removeClass( "ui-focus" );
+ }
+ });
+
+ this.refresh();
+ },
+
+ _cacheVals: function() {
+ this._getInputSet().each(function() {
+ var $this = $(this);
+
+ $this.jqmData( "cacheVal", $this.is( ":checked" ) );
+ });
+ },
+
+ //returns either a set of radios with the same name attribute, or a single checkbox
+ _getInputSet: function(){
+ if(this.inputtype == "checkbox") {
+ return this.element;
+ }
+ return this.element.closest( "form,fieldset,:jqmData(role='page')" )
+ .find( "input[name='"+ this.element.attr( "name" ) +"'][type='"+ this.inputtype +"']" );
+ },
+
+ _updateAll: function() {
+ var self = this;
+
+ this._getInputSet().each(function() {
+ var $this = $(this);
+
+ if ( $this.is( ":checked" ) || self.inputtype === "checkbox" ) {
+ $this.trigger( "change" );
+ }
+ })
+ .checkboxradio( "refresh" );
+ },
+
+ refresh: function() {
+ var input = this.element,
+ label = this.label,
+ icon = label.find( ".ui-icon" );
+
+ // input[0].checked expando doesn't always report the proper value
+ // for checked='checked'
+ if ( $( input[ 0 ] ).prop( "checked" ) ) {
+
+ label.addClass( this.checkedClass ).removeClass( this.uncheckedClass );
+ icon.addClass( this.checkedicon ).removeClass( this.uncheckedicon );
+
+ } else {
+
+ label.removeClass( this.checkedClass ).addClass( this.uncheckedClass );
+ icon.removeClass( this.checkedicon ).addClass( this.uncheckedicon );
+ }
+
+ if ( input.is( ":disabled" ) ) {
+ this.disable();
+ } else {
+ this.enable();
+ }
+ },
+
+ disable: function() {
+ this.element.prop( "disabled", true ).parent().addClass( "ui-disabled" );
+ },
+
+ enable: function() {
+ this.element.prop( "disabled", false ).parent().removeClass( "ui-disabled" );
+ }
+});
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+ $( $.mobile.checkboxradio.prototype.options.initSelector, e.target )
+ .not( ":jqmData(role='none'), :jqmData(role='nojs')" )
+ .checkboxradio();
+});
+
+})( jQuery );
+/*
+* jQuery Mobile Framework : "button" plugin - links that proxy to native input/buttons
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+
+(function( $, undefined ) {
+
+$.widget( "mobile.button", $.mobile.widget, {
+ options: {
+ theme: null,
+ icon: null,
+ iconpos: null,
+ inline: null,
+ corners: true,
+ shadow: true,
+ iconshadow: true,
+ initSelector: "button, [type='button'], [type='submit'], [type='reset'], [type='image']"
+ },
+ _create: function() {
+ var $el = this.element,
+ o = this.options,
+ type,
+ name,
+ $buttonPlaceholder;
+
+ // Add ARIA role
+ this.button = $( "<div></div>" )
+ .text( $el.text() || $el.val() )
+ .buttonMarkup({
+ theme: o.theme,
+ icon: o.icon,
+ iconpos: o.iconpos,
+ inline: o.inline,
+ corners: o.corners,
+ shadow: o.shadow,
+ iconshadow: o.iconshadow
+ })
+ .insertBefore( $el )
+ .append( $el.addClass( "ui-btn-hidden" ) );
+
+ type = $el.attr( "type" );
+ name = $el.attr( "name" );
+
+ // Add hidden input during submit if input type="submit" has a name.
+ if ( type !== "button" && type !== "reset" && name ) {
+ $el.bind( "vclick", function() {
+ // Add hidden input if it doesn’t already exist.
+ if( $buttonPlaceholder === undefined ) {
+ $buttonPlaceholder = $( "<input>", {
+ type: "hidden",
+ name: $el.attr( "name" ),
+ value: $el.attr( "value" )
+ })
+ .insertBefore( $el );
+
+ // Bind to doc to remove after submit handling
+ $( document ).submit(function(){
+ $buttonPlaceholder.remove();
+ });
+ }
+ });
+ }
+
+ this.refresh();
+ },
+
+ enable: function() {
+ this.element.attr( "disabled", false );
+ this.button.removeClass( "ui-disabled" ).attr( "aria-disabled", false );
+ return this._setOption( "disabled", false );
+ },
+
+ disable: function() {
+ this.element.attr( "disabled", true );
+ this.button.addClass( "ui-disabled" ).attr( "aria-disabled", true );
+ return this._setOption( "disabled", true );
+ },
+
+ refresh: function() {
+ if ( this.element.attr( "disabled" ) ) {
+ this.disable();
+ } else {
+ this.enable();
+ }
+ }
+});
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+ $( $.mobile.button.prototype.options.initSelector, e.target )
+ .not( ":jqmData(role='none'), :jqmData(role='nojs')" )
+ .button();
+});
+
+})( jQuery );/*
+* jQuery Mobile Framework : "slider" plugin
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+
+( function( $, undefined ) {
+
+$.widget( "mobile.slider", $.mobile.widget, {
+ options: {
+ theme: null,
+ trackTheme: null,
+ disabled: false,
+ initSelector: "input[type='range'], :jqmData(type='range'), :jqmData(role='slider')"
+ },
+
+ _create: function() {
+
+ // TODO: Each of these should have comments explain what they're for
+ var self = this,
+
+ control = this.element,
+
+ parentTheme = control.parents( "[class*='ui-bar-'],[class*='ui-body-']" ).eq( 0 ),
+
+ parentTheme = parentTheme.length ? parentTheme.attr( "class" ).match( /ui-(bar|body)-([a-z])/ )[ 2 ] : "c",
+
+ theme = this.options.theme ? this.options.theme : parentTheme,
+
+ trackTheme = this.options.trackTheme ? this.options.trackTheme : parentTheme,
+
+ cType = control[ 0 ].nodeName.toLowerCase(),
+
+ selectClass = ( cType == "select" ) ? "ui-slider-switch" : "",
+
+ controlID = control.attr( "id" ),
+
+ labelID = controlID + "-label",
+
+ label = $( "[for='"+ controlID +"']" ).attr( "id", labelID ),
+
+ val = function() {
+ return cType == "input" ? parseFloat( control.val() ) : control[0].selectedIndex;
+ },
+
+ min = cType == "input" ? parseFloat( control.attr( "min" ) ) : 0,
+
+ max = cType == "input" ? parseFloat( control.attr( "max" ) ) : control.find( "option" ).length-1,
+
+ step = window.parseFloat( control.attr( "step" ) || 1 ),
+
+ slider = $( "<div class='ui-slider " + selectClass + " ui-btn-down-" + trackTheme +
+ " ui-btn-corner-all' role='application'></div>" ),
+
+ handle = $( "<a href='#' class='ui-slider-handle'></a>" )
+ .appendTo( slider )
+ .buttonMarkup({ corners: true, theme: theme, shadow: true })
+ .attr({
+ "role": "slider",
+ "aria-valuemin": min,
+ "aria-valuemax": max,
+ "aria-valuenow": val(),
+ "aria-valuetext": val(),
+ "title": val(),
+ "aria-labelledby": labelID
+ }),
+ options;
+
+ $.extend( this, {
+ slider: slider,
+ handle: handle,
+ dragging: false,
+ beforeStart: null,
+ userModified: false
+ });
+
+ if ( cType == "select" ) {
+
+ slider.wrapInner( "<div class='ui-slider-inneroffset'></div>" );
+
+ options = control.find( "option" );
+
+ control.find( "option" ).each(function( i ) {
+
+ var side = !i ? "b":"a",
+ corners = !i ? "right" :"left",
+ theme = !i ? " ui-btn-down-" + trackTheme :( " " + $.mobile.activeBtnClass );
+
+ $( "<div class='ui-slider-labelbg ui-slider-labelbg-" + side + theme + " ui-btn-corner-" + corners + "'></div>" )
+ .prependTo( slider );
+
+ $( "<span class='ui-slider-label ui-slider-label-" + side + theme + " ui-btn-corner-" + corners + "' role='img'>" + $( this ).text() + "</span>" )
+ .prependTo( handle );
+ });
+
+ }
+
+ label.addClass( "ui-slider" );
+
+ // monitor the input for updated values
+ control.addClass( cType === "input" ? "ui-slider-input" : "ui-slider-switch" )
+ .change( function() {
+ self.refresh( val(), true );
+ })
+ .keyup( function() { // necessary?
+ self.refresh( val(), true, true );
+ })
+ .blur( function() {
+ self.refresh( val(), true );
+ });
+
+ // prevent screen drag when slider activated
+ $( document ).bind( "vmousemove", function( event ) {
+ if ( self.dragging ) {
+ self.refresh( event );
+ self.userModified = self.userModified || self.beforeStart !== control[0].selectedIndex;
+ return false;
+ }
+ });
+
+ slider.bind( "vmousedown", function( event ) {
+ self.dragging = true;
+ self.userModified = false;
+
+ if ( cType === "select" ) {
+ self.beforeStart = control[0].selectedIndex;
+ }
+ self.refresh( event );
+ return false;
+ });
+
+ slider.add( document )
+ .bind( "vmouseup", function() {
+ if ( self.dragging ) {
+
+ self.dragging = false;
+
+ if ( cType === "select" ) {
+
+ if ( !self.userModified ) {
+ //tap occurred, but value didn't change. flip it!
+ handle.addClass( "ui-slider-handle-snapping" );
+ self.refresh( !self.beforeStart ? 1 : 0 );
+ }
+ }
+ return false;
+ }
+ });
+
+ slider.insertAfter( control );
+
+ // NOTE force focus on handle
+ this.handle
+ .bind( "vmousedown", function() {
+ $( this ).focus();
+ })
+ .bind( "vclick", false );
+
+ this.handle
+ .bind( "keydown", function( event ) {
+ var index = val();
+
+ if ( self.options.disabled ) {
+ return;
+ }
+
+ // In all cases prevent the default and mark the handle as active
+ switch ( event.keyCode ) {
+ case $.mobile.keyCode.HOME:
+ case $.mobile.keyCode.END:
+ case $.mobile.keyCode.PAGE_UP:
+ case $.mobile.keyCode.PAGE_DOWN:
+ case $.mobile.keyCode.UP:
+ case $.mobile.keyCode.RIGHT:
+ case $.mobile.keyCode.DOWN:
+ case $.mobile.keyCode.LEFT:
+ event.preventDefault();
+
+ if ( !self._keySliding ) {
+ self._keySliding = true;
+ $( this ).addClass( "ui-state-active" );
+ }
+ break;
+ }
+
+ // move the slider according to the keypress
+ switch ( event.keyCode ) {
+ case $.mobile.keyCode.HOME:
+ self.refresh( min );
+ break;
+ case $.mobile.keyCode.END:
+ self.refresh( max );
+ break;
+ case $.mobile.keyCode.PAGE_UP:
+ case $.mobile.keyCode.UP:
+ case $.mobile.keyCode.RIGHT:
+ self.refresh( index + step );
+ break;
+ case $.mobile.keyCode.PAGE_DOWN:
+ case $.mobile.keyCode.DOWN:
+ case $.mobile.keyCode.LEFT:
+ self.refresh( index - step );
+ break;
+ }
+ }) // remove active mark
+ .keyup( function( event ) {
+ if ( self._keySliding ) {
+ self._keySliding = false;
+ $( this ).removeClass( "ui-state-active" );
+ }
+ });
+
+ this.refresh(undefined, undefined, true);
+ },
+
+ refresh: function( val, isfromControl, preventInputUpdate ) {
+ if ( this.options.disabled ) { return; }
+
+ var control = this.element, percent,
+ cType = control[0].nodeName.toLowerCase(),
+ min = cType === "input" ? parseFloat( control.attr( "min" ) ) : 0,
+ max = cType === "input" ? parseFloat( control.attr( "max" ) ) : control.find( "option" ).length - 1;
+
+ if ( typeof val === "object" ) {
+ var data = val,
+ // a slight tolerance helped get to the ends of the slider
+ tol = 8;
+ if ( !this.dragging ||
+ data.pageX < this.slider.offset().left - tol ||
+ data.pageX > this.slider.offset().left + this.slider.width() + tol ) {
+ return;
+ }
+ percent = Math.round( ( ( data.pageX - this.slider.offset().left ) / this.slider.width() ) * 100 );
+ } else {
+ if ( val == null ) {
+ val = cType === "input" ? parseFloat( control.val() ) : control[0].selectedIndex;
+ }
+ percent = ( parseFloat( val ) - min ) / ( max - min ) * 100;
+ }
+
+ if ( isNaN( percent ) ) {
+ return;
+ }
+
+ if ( percent < 0 ) {
+ percent = 0;
+ }
+
+ if ( percent > 100 ) {
+ percent = 100;
+ }
+
+ var newval = Math.round( ( percent / 100 ) * ( max - min ) ) + min;
+
+ if ( newval < min ) {
+ newval = min;
+ }
+
+ if ( newval > max ) {
+ newval = max;
+ }
+
+ // Flip the stack of the bg colors
+ if ( percent > 60 && cType === "select" ) {
+ // TODO: Dead path?
+ }
+ this.handle.css( "left", percent + "%" );
+ this.handle.attr( {
+ "aria-valuenow": cType === "input" ? newval : control.find( "option" ).eq( newval ).attr( "value" ),
+ "aria-valuetext": cType === "input" ? newval : control.find( "option" ).eq( newval ).text(),
+ title: newval
+ });
+
+ // add/remove classes for flip toggle switch
+ if ( cType === "select" ) {
+ if ( newval === 0 ) {
+ this.slider.addClass( "ui-slider-switch-a" )
+ .removeClass( "ui-slider-switch-b" );
+ } else {
+ this.slider.addClass( "ui-slider-switch-b" )
+ .removeClass( "ui-slider-switch-a" );
+ }
+ }
+
+ if ( !preventInputUpdate ) {
+ var valueChanged = false;
+
+ // update control"s value
+ if ( cType === "input" ) {
+ valueChanged = control.val() !== newval;
+ control.val( newval );
+ } else {
+ valueChanged = control[ 0 ].selectedIndex !== newval;
+ control[ 0 ].selectedIndex = newval;
+ }
+ if ( !isfromControl && valueChanged ) {
+ control.trigger( "change" );
+ }
+ }
+ },
+
+ enable: function() {
+ this.element.attr( "disabled", false );
+ this.slider.removeClass( "ui-disabled" ).attr( "aria-disabled", false );
+ return this._setOption( "disabled", false );
+ },
+
+ disable: function() {
+ this.element.attr( "disabled", true );
+ this.slider.addClass( "ui-disabled" ).attr( "aria-disabled", true );
+ return this._setOption( "disabled", true );
+ }
+
+});
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+
+ $( $.mobile.slider.prototype.options.initSelector, e.target )
+ .not( ":jqmData(role='none'), :jqmData(role='nojs')" )
+ .slider();
+
+});
+
+})( jQuery );
+/*
+* jQuery Mobile Framework : "textinput" plugin for text inputs, textareas
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+
+(function( $, undefined ) {
+
+$.widget( "mobile.textinput", $.mobile.widget, {
+ options: {
+ theme: null,
+ initSelector: "input[type='text'], input[type='search'], :jqmData(type='search'), input[type='number'], :jqmData(type='number'), input[type='password'], input[type='email'], input[type='url'], input[type='tel'], textarea, input:not([type])"
+ },
+
+ _create: function() {
+
+ var input = this.element,
+ o = this.options,
+ theme = o.theme,
+ themedParent, themeclass, themeLetter, focusedEl, clearbtn;
+
+ if ( !theme ) {
+ themedParent = this.element.closest( "[class*='ui-bar-'],[class*='ui-body-']" );
+ themeLetter = themedParent.length && /ui-(bar|body)-([a-z])/.exec( themedParent.attr( "class" ) );
+ theme = themeLetter && themeLetter[2] || "c";
+ }
+
+ themeclass = " ui-body-" + theme;
+
+ $( "label[for='" + input.attr( "id" ) + "']" ).addClass( "ui-input-text" );
+
+ input.addClass("ui-input-text ui-body-"+ o.theme );
+
+ focusedEl = input;
+
+ // XXX: Temporary workaround for issue 785 (Apple bug 8910589).
+ // Turn off autocorrect and autocomplete on non-iOS 5 devices
+ // since the popup they use can't be dismissed by the user. Note
+ // that we test for the presence of the feature by looking for
+ // the autocorrect property on the input element. We currently
+ // have no test for iOS 5 or newer so we're temporarily using
+ // the touchOverflow support flag for jQM 1.0. Yes, I feel dirty. - jblas
+ if ( typeof input[0].autocorrect !== "undefined" && !$.support.touchOverflow ) {
+ // Set the attribute instead of the property just in case there
+ // is code that attempts to make modifications via HTML.
+ input[0].setAttribute( "autocorrect", "off" );
+ input[0].setAttribute( "autocomplete", "off" );
+ }
+
+
+ //"search" input widget
+ if ( input.is( "[type='search'],:jqmData(type='search')" ) ) {
+
+ focusedEl = input.wrap( "<div class='ui-input-search ui-shadow-inset ui-btn-corner-all ui-btn-shadow ui-icon-searchfield" + themeclass + "'></div>" ).parent();
+ clearbtn = $( "<a href='#' class='ui-input-clear' title='clear text'>clear text</a>" )
+ .tap(function( event ) {
+ input.val( "" ).focus();
+ input.trigger( "change" );
+ clearbtn.addClass( "ui-input-clear-hidden" );
+ event.preventDefault();
+ })
+ .appendTo( focusedEl )
+ .buttonMarkup({
+ icon: "delete",
+ iconpos: "notext",
+ corners: true,
+ shadow: true
+ });
+
+ function toggleClear() {
+ if ( !input.val() ) {
+ clearbtn.addClass( "ui-input-clear-hidden" );
+ } else {
+ clearbtn.removeClass( "ui-input-clear-hidden" );
+ }
+ }
+
+ toggleClear();
+
+ input.keyup( toggleClear )
+ .focus( toggleClear );
+
+ } else {
+ input.addClass( "ui-corner-all ui-shadow-inset" + themeclass );
+ }
+
+ input.focus(function() {
+ focusedEl.addClass( "ui-focus" );
+ })
+ .blur(function(){
+ focusedEl.removeClass( "ui-focus" );
+ });
+
+ // Autogrow
+ if ( input.is( "textarea" ) ) {
+ var extraLineHeight = 15,
+ keyupTimeoutBuffer = 100,
+ keyup = function() {
+ var scrollHeight = input[ 0 ].scrollHeight,
+ clientHeight = input[ 0 ].clientHeight;
+
+ if ( clientHeight < scrollHeight ) {
+ input.css({
+ height: (scrollHeight + extraLineHeight)
+ });
+ }
+ },
+ keyupTimeout;
+
+ input.keyup(function() {
+ clearTimeout( keyupTimeout );
+ keyupTimeout = setTimeout( keyup, keyupTimeoutBuffer );
+ });
+ }
+ },
+
+ disable: function(){
+ ( this.element.attr( "disabled", true ).is( "[type='search'],:jqmData(type='search')" ) ?
+ this.element.parent() : this.element ).addClass( "ui-disabled" );
+ },
+
+ enable: function(){
+ ( this.element.attr( "disabled", false).is( "[type='search'],:jqmData(type='search')" ) ?
+ this.element.parent() : this.element ).removeClass( "ui-disabled" );
+ }
+});
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+
+ $( $.mobile.textinput.prototype.options.initSelector, e.target )
+ .not( ":jqmData(role='none'), :jqmData(role='nojs')" )
+ .textinput();
+
+});
+
+})( jQuery );
+/*
+* jQuery Mobile Framework : custom "selectmenu" plugin
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+
+(function( $, undefined ) {
+ var extendSelect = function( widget ){
+
+ var select = widget.select,
+ selectID = widget.selectID,
+ label = widget.label,
+ thisPage = widget.select.closest( ".ui-page" ),
+ screen = $( "<div>", {"class": "ui-selectmenu-screen ui-screen-hidden"} ).appendTo( thisPage ),
+ selectOptions = widget._selectOptions(),
+ isMultiple = widget.isMultiple = widget.select[ 0 ].multiple,
+ buttonId = selectID + "-button",
+ menuId = selectID + "-menu",
+ menuPage = $( "<div data-" + $.mobile.ns + "role='dialog' data-" +$.mobile.ns + "theme='"+ widget.options.menuPageTheme +"'>" +
+ "<div data-" + $.mobile.ns + "role='header'>" +
+ "<div class='ui-title'>" + label.getEncodedText() + "</div>"+
+ "</div>"+
+ "<div data-" + $.mobile.ns + "role='content'></div>"+
+ "</div>" ).appendTo( $.mobile.pageContainer ).page(),
+
+ listbox = $("<div>", { "class": "ui-selectmenu ui-selectmenu-hidden ui-overlay-shadow ui-corner-all ui-body-" + widget.options.overlayTheme + " " + $.mobile.defaultDialogTransition } ).insertAfter(screen),
+
+ list = $( "<ul>", {
+ "class": "ui-selectmenu-list",
+ "id": menuId,
+ "role": "listbox",
+ "aria-labelledby": buttonId
+ }).attr( "data-" + $.mobile.ns + "theme", widget.options.theme ).appendTo( listbox ),
+
+ header = $( "<div>", {
+ "class": "ui-header ui-bar-" + widget.options.theme
+ }).prependTo( listbox ),
+
+ headerTitle = $( "<h1>", {
+ "class": "ui-title"
+ }).appendTo( header ),
+
+ headerClose = $( "<a>", {
+ "text": widget.options.closeText,
+ "href": "#",
+ "class": "ui-btn-left"
+ }).attr( "data-" + $.mobile.ns + "iconpos", "notext" ).attr( "data-" + $.mobile.ns + "icon", "delete" ).appendTo( header ).buttonMarkup(),
+
+ menuPageContent = menuPage.find( ".ui-content" ),
+
+ menuPageClose = menuPage.find( ".ui-header a" );
+
+
+ $.extend( widget, {
+ select: widget.select,
+ selectID: selectID,
+ buttonId: buttonId,
+ menuId: menuId,
+ thisPage: thisPage,
+ menuPage: menuPage,
+ label: label,
+ screen: screen,
+ selectOptions: selectOptions,
+ isMultiple: isMultiple,
+ theme: widget.options.theme,
+ listbox: listbox,
+ list: list,
+ header: header,
+ headerTitle: headerTitle,
+ headerClose: headerClose,
+ menuPageContent: menuPageContent,
+ menuPageClose: menuPageClose,
+ placeholder: "",
+
+ build: function() {
+ var self = this;
+
+ // Create list from select, update state
+ self.refresh();
+
+ self.select.attr( "tabindex", "-1" ).focus(function() {
+ $( this ).blur();
+ self.button.focus();
+ });
+
+ // Button events
+ self.button.bind( "vclick keydown" , function( event ) {
+ if ( event.type == "vclick" ||
+ event.keyCode && ( event.keyCode === $.mobile.keyCode.ENTER ||
+ event.keyCode === $.mobile.keyCode.SPACE ) ) {
+
+ self.open();
+ event.preventDefault();
+ }
+ });
+
+ // Events for list items
+ self.list.attr( "role", "listbox" )
+ .delegate( ".ui-li>a", "focusin", function() {
+ $( this ).attr( "tabindex", "0" );
+ })
+ .delegate( ".ui-li>a", "focusout", function() {
+ $( this ).attr( "tabindex", "-1" );
+ })
+ .delegate( "li:not(.ui-disabled, .ui-li-divider)", "click", function( event ) {
+
+ // index of option tag to be selected
+ var oldIndex = self.select[ 0 ].selectedIndex,
+ newIndex = self.list.find( "li:not(.ui-li-divider)" ).index( this ),
+ option = self._selectOptions().eq( newIndex )[ 0 ];
+
+ // toggle selected status on the tag for multi selects
+ option.selected = self.isMultiple ? !option.selected : true;
+
+ // toggle checkbox class for multiple selects
+ if ( self.isMultiple ) {
+ $( this ).find( ".ui-icon" )
+ .toggleClass( "ui-icon-checkbox-on", option.selected )
+ .toggleClass( "ui-icon-checkbox-off", !option.selected );
+ }
+
+ // trigger change if value changed
+ if ( self.isMultiple || oldIndex !== newIndex ) {
+ self.select.trigger( "change" );
+ }
+
+ //hide custom select for single selects only
+ if ( !self.isMultiple ) {
+ self.close();
+ }
+
+ event.preventDefault();
+ })
+ .keydown(function( event ) { //keyboard events for menu items
+ var target = $( event.target ),
+ li = target.closest( "li" ),
+ prev, next;
+
+ // switch logic based on which key was pressed
+ switch ( event.keyCode ) {
+ // up or left arrow keys
+ case 38:
+ prev = li.prev();
+
+ // if there's a previous option, focus it
+ if ( prev.length ) {
+ target
+ .blur()
+ .attr( "tabindex", "-1" );
+
+ prev.find( "a" ).first().focus();
+ }
+
+ return false;
+ break;
+
+ // down or right arrow keys
+ case 40:
+ next = li.next();
+
+ // if there's a next option, focus it
+ if ( next.length ) {
+ target
+ .blur()
+ .attr( "tabindex", "-1" );
+
+ next.find( "a" ).first().focus();
+ }
+
+ return false;
+ break;
+
+ // If enter or space is pressed, trigger click
+ case 13:
+ case 32:
+ target.trigger( "click" );
+
+ return false;
+ break;
+ }
+ });
+
+ // button refocus ensures proper height calculation
+ // by removing the inline style and ensuring page inclusion
+ self.menuPage.bind( "pagehide", function() {
+ self.list.appendTo( self.listbox );
+ self._focusButton();
+
+ // TODO centralize page removal binding / handling in the page plugin.
+ // Suggestion from @jblas to do refcounting
+ //
+ // TODO extremely confusing dependency on the open method where the pagehide.remove
+ // bindings are stripped to prevent the parent page from disappearing. The way
+ // we're keeping pages in the DOM right now sucks
+ //
+ // rebind the page remove that was unbound in the open function
+ // to allow for the parent page removal from actions other than the use
+ // of a dialog sized custom select
+ //
+ // doing this here provides for the back button on the custom select dialog
+ $.mobile._bindPageRemove.call( self.thisPage );
+ });
+
+ // Events on "screen" overlay
+ self.screen.bind( "vclick", function( event ) {
+ self.close();
+ });
+
+ // Close button on small overlays
+ self.headerClose.click( function() {
+ if ( self.menuType == "overlay" ) {
+ self.close();
+ return false;
+ }
+ });
+
+ // track this dependency so that when the parent page
+ // is removed on pagehide it will also remove the menupage
+ self.thisPage.addDependents( this.menuPage );
+ },
+
+ _isRebuildRequired: function() {
+ var list = this.list.find( "li" ),
+ options = this._selectOptions();
+
+ // TODO exceedingly naive method to determine difference
+ // ignores value changes etc in favor of a forcedRebuild
+ // from the user in the refresh method
+ return options.text() !== list.text();
+ },
+
+ refresh: function( forceRebuild , foo ){
+ var self = this,
+ select = this.element,
+ isMultiple = this.isMultiple,
+ options = this._selectOptions(),
+ selected = this.selected(),
+ // return an array of all selected index's
+ indicies = this.selectedIndices();
+
+ if ( forceRebuild || this._isRebuildRequired() ) {
+ self._buildList();
+ }
+
+ self.setButtonText();
+ self.setButtonCount();
+
+ self.list.find( "li:not(.ui-li-divider)" )
+ .removeClass( $.mobile.activeBtnClass )
+ .attr( "aria-selected", false )
+ .each(function( i ) {
+
+ if ( $.inArray( i, indicies ) > -1 ) {
+ var item = $( this );
+
+ // Aria selected attr
+ item.attr( "aria-selected", true );
+
+ // Multiple selects: add the "on" checkbox state to the icon
+ if ( self.isMultiple ) {
+ item.find( ".ui-icon" ).removeClass( "ui-icon-checkbox-off" ).addClass( "ui-icon-checkbox-on" );
+ } else {
+ item.addClass( $.mobile.activeBtnClass );
+ }
+ }
+ });
+ },
+
+ close: function() {
+ if ( this.options.disabled || !this.isOpen ) {
+ return;
+ }
+
+ var self = this;
+
+ if ( self.menuType == "page" ) {
+ // doesn't solve the possible issue with calling change page
+ // where the objects don't define data urls which prevents dialog key
+ // stripping - changePage has incoming refactor
+ window.history.back();
+ } else {
+ self.screen.addClass( "ui-screen-hidden" );
+ self.listbox.addClass( "ui-selectmenu-hidden" ).removeAttr( "style" ).removeClass( "in" );
+ self.list.appendTo( self.listbox );
+ self._focusButton();
+ }
+
+ // allow the dialog to be closed again
+ self.isOpen = false;
+ },
+
+ open: function() {
+ if ( this.options.disabled ) {
+ return;
+ }
+
+ var self = this,
+ menuHeight = self.list.parent().outerHeight(),
+ menuWidth = self.list.parent().outerWidth(),
+ activePage = $( ".ui-page-active" ),
+ tOverflow = $.support.touchOverflow && $.mobile.touchOverflowEnabled,
+ tScrollElem = activePage.is( ".ui-native-fixed" ) ? activePage.find( ".ui-content" ) : activePage;
+ scrollTop = tOverflow ? tScrollElem.scrollTop() : $( window ).scrollTop(),
+ btnOffset = self.button.offset().top,
+ screenHeight = window.innerHeight,
+ screenWidth = window.innerWidth;
+
+ //add active class to button
+ self.button.addClass( $.mobile.activeBtnClass );
+
+ //remove after delay
+ setTimeout( function() {
+ self.button.removeClass( $.mobile.activeBtnClass );
+ }, 300);
+
+ function focusMenuItem() {
+ self.list.find( $.mobile.activeBtnClass ).focus();
+ }
+
+ if ( menuHeight > screenHeight - 80 || !$.support.scrollTop ) {
+ // prevent the parent page from being removed from the DOM,
+ // otherwise the results of selecting a list item in the dialog
+ // fall into a black hole
+ self.thisPage.unbind( "pagehide.remove" );
+
+ //for webos (set lastscroll using button offset)
+ if ( scrollTop == 0 && btnOffset > screenHeight ) {
+ self.thisPage.one( "pagehide", function() {
+ $( this ).jqmData( "lastScroll", btnOffset );
+ });
+ }
+
+ self.menuPage.one( "pageshow", function() {
+ // silentScroll() is called whenever a page is shown to restore
+ // any previous scroll position the page may have had. We need to
+ // wait for the "silentscroll" event before setting focus to avoid
+ // the browser"s "feature" which offsets rendering to make sure
+ // whatever has focus is in view.
+ $( window ).one( "silentscroll", function() {
+ focusMenuItem();
+ });
+
+ self.isOpen = true;
+ });
+
+ self.menuType = "page";
+ self.menuPageContent.append( self.list );
+ $.mobile.changePage( self.menuPage, {
+ transition: $.mobile.defaultDialogTransition
+ });
+ } else {
+ self.menuType = "overlay";
+
+ self.screen.height( $(document).height() )
+ .removeClass( "ui-screen-hidden" );
+
+ // Try and center the overlay over the button
+ var roomtop = btnOffset - scrollTop,
+ roombot = scrollTop + screenHeight - btnOffset,
+ halfheight = menuHeight / 2,
+ maxwidth = parseFloat( self.list.parent().css( "max-width" ) ),
+ newtop, newleft;
+
+ if ( roomtop > menuHeight / 2 && roombot > menuHeight / 2 ) {
+ newtop = btnOffset + ( self.button.outerHeight() / 2 ) - halfheight;
+ } else {
+ // 30px tolerance off the edges
+ newtop = roomtop > roombot ? scrollTop + screenHeight - menuHeight - 30 : scrollTop + 30;
+ }
+
+ // If the menuwidth is smaller than the screen center is
+ if ( menuWidth < maxwidth ) {
+ newleft = ( screenWidth - menuWidth ) / 2;
+ } else {
+
+ //otherwise insure a >= 30px offset from the left
+ newleft = self.button.offset().left + self.button.outerWidth() / 2 - menuWidth / 2;
+
+ // 30px tolerance off the edges
+ if ( newleft < 30 ) {
+ newleft = 30;
+ } else if ( (newleft + menuWidth) > screenWidth ) {
+ newleft = screenWidth - menuWidth - 30;
+ }
+ }
+
+ self.listbox.append( self.list )
+ .removeClass( "ui-selectmenu-hidden" )
+ .css({
+ top: newtop,
+ left: newleft
+ })
+ .addClass( "in" );
+
+ focusMenuItem();
+
+ // duplicate with value set in page show for dialog sized selects
+ self.isOpen = true;
+ }
+ },
+
+ _buildList: function() {
+ var self = this,
+ o = this.options,
+ placeholder = this.placeholder,
+ optgroups = [],
+ lis = [],
+ dataIcon = self.isMultiple ? "checkbox-off" : "false";
+
+ self.list.empty().filter( ".ui-listview" ).listview( "destroy" );
+
+ // Populate menu with options from select element
+ self.select.find( "option" ).each( function( i ) {
+ var $this = $( this ),
+ $parent = $this.parent(),
+ text = $this.getEncodedText(),
+ anchor = "<a href='#'>"+ text +"</a>",
+ classes = [],
+ extraAttrs = [];
+
+ // Are we inside an optgroup?
+ if ( $parent.is( "optgroup" ) ) {
+ var optLabel = $parent.attr( "label" );
+
+ // has this optgroup already been built yet?
+ if ( $.inArray( optLabel, optgroups ) === -1 ) {
+ lis.push( "<li data-" + $.mobile.ns + "role='list-divider'>"+ optLabel +"</li>" );
+ optgroups.push( optLabel );
+ }
+ }
+
+ // Find placeholder text
+ // TODO: Are you sure you want to use getAttribute? ^RW
+ if ( !this.getAttribute( "value" ) || text.length == 0 || $this.jqmData( "placeholder" ) ) {
+ if ( o.hidePlaceholderMenuItems ) {
+ classes.push( "ui-selectmenu-placeholder" );
+ }
+ placeholder = self.placeholder = text;
+ }
+
+ // support disabled option tags
+ if ( this.disabled ) {
+ classes.push( "ui-disabled" );
+ extraAttrs.push( "aria-disabled='true'" );
+ }
+
+ lis.push( "<li data-" + $.mobile.ns + "option-index='" + i + "' data-" + $.mobile.ns + "icon='"+ dataIcon +"' class='"+ classes.join(" ") + "' " + extraAttrs.join(" ") +">"+ anchor +"</li>" );
+ });
+
+ self.list.html( lis.join(" ") );
+
+ self.list.find( "li" )
+ .attr({ "role": "option", "tabindex": "-1" })
+ .first().attr( "tabindex", "0" );
+
+ // Hide header close link for single selects
+ if ( !this.isMultiple ) {
+ this.headerClose.hide();
+ }
+
+ // Hide header if it's not a multiselect and there's no placeholder
+ if ( !this.isMultiple && !placeholder.length ) {
+ this.header.hide();
+ } else {
+ this.headerTitle.text( this.placeholder );
+ }
+
+ // Now populated, create listview
+ self.list.listview();
+ },
+
+ _button: function(){
+ return $( "<a>", {
+ "href": "#",
+ "role": "button",
+ // TODO value is undefined at creation
+ "id": this.buttonId,
+ "aria-haspopup": "true",
+
+ // TODO value is undefined at creation
+ "aria-owns": this.menuId
+ });
+ }
+ });
+ };
+
+ $( "select" ).live( "selectmenubeforecreate", function(){
+ var selectmenuWidget = $( this ).data( "selectmenu" );
+
+ if( !selectmenuWidget.options.nativeMenu ){
+ extendSelect( selectmenuWidget );
+ }
+ });
+})( jQuery );
+/*
+* jQuery Mobile Framework : "selectmenu" plugin
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+
+(function( $, undefined ) {
+
+$.widget( "mobile.selectmenu", $.mobile.widget, {
+ options: {
+ theme: null,
+ disabled: false,
+ icon: "arrow-d",
+ iconpos: "right",
+ inline: null,
+ corners: true,
+ shadow: true,
+ iconshadow: true,
+ menuPageTheme: "b",
+ overlayTheme: "a",
+ hidePlaceholderMenuItems: true,
+ closeText: "Close",
+ nativeMenu: true,
+ initSelector: "select:not(:jqmData(role='slider'))"
+ },
+
+ _button: function(){
+ return $( "<div/>" );
+ },
+
+ _theme: function(){
+ if ( this.options.theme ){
+ return this.options.theme;
+ }
+
+ var themedParent, theme;
+ // if no theme is defined, try to find closest theme container
+ // TODO move to core as something like findCurrentTheme
+ themedParent = this.select.closest( "[class*='ui-bar-'], [class*='ui-body-']" );
+ theme = themedParent.length ?
+ /ui-(bar|body)-([a-z])/.exec( themedParent.attr( "class" ) )[2] :
+ "c";
+
+ return theme;
+ },
+
+ _setDisabled: function( value ) {
+ this.element.attr( "disabled", value );
+ this.button.attr( "aria-disabled", value );
+ return this._setOption( "disabled", value );
+ },
+
+ _focusButton : function() {
+ var self = this;
+
+ setTimeout( function() {
+ self.button.focus();
+ }, 40);
+ },
+
+ _selectOptions: function() {
+ return this.select.find( "option" );
+ },
+
+ // setup items that are generally necessary for select menu extension
+ _preExtension: function(){
+ this.select = this.element.wrap( "<div class='ui-select'>" );
+ this.selectID = this.select.attr( "id" );
+ this.label = $( "label[for='"+ this.selectID +"']" ).addClass( "ui-select" );
+ this.isMultiple = this.select[ 0 ].multiple;
+ this.options.theme = this._theme();
+ },
+
+ _create: function() {
+ this._preExtension();
+
+ // Allows for extension of the native select for custom selects and other plugins
+ // see select.custom for example extension
+ // TODO explore plugin registration
+ this._trigger( "beforeCreate" );
+
+ this.button = this._button();
+
+ var self = this,
+
+ options = this.options,
+
+ // IE throws an exception at options.item() function when
+ // there is no selected item
+ // select first in this case
+ selectedIndex = this.select[ 0 ].selectedIndex == -1 ? 0 : this.select[ 0 ].selectedIndex,
+
+ // TODO values buttonId and menuId are undefined here
+ button = this.button
+ .text( $( this.select[ 0 ].options.item( selectedIndex ) ).text() )
+ .insertBefore( this.select )
+ .buttonMarkup( {
+ theme: options.theme,
+ icon: options.icon,
+ iconpos: options.iconpos,
+ inline: options.inline,
+ corners: options.corners,
+ shadow: options.shadow,
+ iconshadow: options.iconshadow
+ });
+
+ // Opera does not properly support opacity on select elements
+ // In Mini, it hides the element, but not its text
+ // On the desktop,it seems to do the opposite
+ // for these reasons, using the nativeMenu option results in a full native select in Opera
+ if ( options.nativeMenu && window.opera && window.opera.version ) {
+ this.select.addClass( "ui-select-nativeonly" );
+ }
+
+ // Add counter for multi selects
+ if ( this.isMultiple ) {
+ this.buttonCount = $( "<span>" )
+ .addClass( "ui-li-count ui-btn-up-c ui-btn-corner-all" )
+ .hide()
+ .appendTo( button.addClass('ui-li-has-count') );
+ }
+
+ // Disable if specified
+ if ( options.disabled ) {
+ this.disable();
+ }
+
+ // Events on native select
+ this.select.change( function() {
+ self.refresh();
+ });
+
+ this.build();
+ },
+
+ build: function() {
+ var self = this;
+
+ this.select
+ .appendTo( self.button )
+ .bind( "vmousedown", function() {
+ // Add active class to button
+ self.button.addClass( $.mobile.activeBtnClass );
+ })
+ .bind( "focus vmouseover", function() {
+ self.button.trigger( "vmouseover" );
+ })
+ .bind( "vmousemove", function() {
+ // Remove active class on scroll/touchmove
+ self.button.removeClass( $.mobile.activeBtnClass );
+ })
+ .bind( "change blur vmouseout", function() {
+ self.button.trigger( "vmouseout" )
+ .removeClass( $.mobile.activeBtnClass );
+ })
+ .bind( "change blur", function() {
+ self.button.removeClass( "ui-btn-down-" + self.options.theme );
+ });
+ },
+
+ selected: function() {
+ return this._selectOptions().filter( ":selected" );
+ },
+
+ selectedIndices: function() {
+ var self = this;
+
+ return this.selected().map( function() {
+ return self._selectOptions().index( this );
+ }).get();
+ },
+
+ setButtonText: function() {
+ var self = this, selected = this.selected();
+
+ this.button.find( ".ui-btn-text" ).text( function() {
+ if ( !self.isMultiple ) {
+ return selected.text();
+ }
+
+ return selected.length ? selected.map( function() {
+ return $( this ).text();
+ }).get().join( ", " ) : self.placeholder;
+ });
+ },
+
+ setButtonCount: function() {
+ var selected = this.selected();
+
+ // multiple count inside button
+ if ( this.isMultiple ) {
+ this.buttonCount[ selected.length > 1 ? "show" : "hide" ]().text( selected.length );
+ }
+ },
+
+ refresh: function() {
+ this.setButtonText();
+ this.setButtonCount();
+ },
+
+ // open and close preserved in native selects
+ // to simplify users code when looping over selects
+ open: $.noop,
+ close: $.noop,
+
+ disable: function() {
+ this._setDisabled( true );
+ this.button.addClass( "ui-disabled" );
+ },
+
+ enable: function() {
+ this._setDisabled( false );
+ this.button.removeClass( "ui-disabled" );
+ }
+});
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+ $( $.mobile.selectmenu.prototype.options.initSelector, e.target )
+ .not( ":jqmData(role='none'), :jqmData(role='nojs')" )
+ .selectmenu();
+});
+})( jQuery );
+/*
+* jQuery Mobile Framework : plugin for making button-like links
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+( function( $, undefined ) {
+
+$.fn.buttonMarkup = function( options ) {
+ return this.each( function() {
+ var el = $( this ),
+ o = $.extend( {}, $.fn.buttonMarkup.defaults, {
+ icon: el.jqmData( "icon" ),
+ iconpos: el.jqmData( "iconpos" ),
+ theme: el.jqmData( "theme" ),
+ inline: el.jqmData( "inline" )
+ }, options ),
+
+ // Classes Defined
+ innerClass = "ui-btn-inner",
+ buttonClass, iconClass,
+ themedParent, wrap;
+
+ if ( attachEvents ) {
+ attachEvents();
+ }
+
+ // if not, try to find closest theme container
+ if ( !o.theme ) {
+ themedParent = el.closest( "[class*='ui-bar-'],[class*='ui-body-']" );
+ o.theme = themedParent.length ?
+ /ui-(bar|body)-([a-z])/.exec( themedParent.attr( "class" ) )[2] :
+ "c";
+ }
+
+ buttonClass = "ui-btn ui-btn-up-" + o.theme;
+
+ if ( o.inline ) {
+ buttonClass += " ui-btn-inline";
+ }
+
+ if ( o.icon ) {
+ o.icon = "ui-icon-" + o.icon;
+ o.iconpos = o.iconpos || "left";
+
+ iconClass = "ui-icon " + o.icon;
+
+ if ( o.iconshadow ) {
+ iconClass += " ui-icon-shadow";
+ }
+ }
+
+ if ( o.iconpos ) {
+ buttonClass += " ui-btn-icon-" + o.iconpos;
+
+ if ( o.iconpos == "notext" && !el.attr( "title" ) ) {
+ el.attr( "title", el.text() );
+ }
+ }
+
+ if ( o.corners ) {
+ buttonClass += " ui-btn-corner-all";
+ innerClass += " ui-btn-corner-all";
+ }
+
+ if ( o.shadow ) {
+ buttonClass += " ui-shadow";
+ }
+
+ el.attr( "data-" + $.mobile.ns + "theme", o.theme )
+ .addClass( buttonClass );
+
+ wrap = ( "<D class='" + innerClass + "'><D class='ui-btn-text'></D>" +
+ ( o.icon ? "<span class='" + iconClass + "'></span>" : "" ) +
+ "</D>" ).replace( /D/g, o.wrapperEls );
+
+ el.wrapInner( wrap );
+ });
+};
+
+$.fn.buttonMarkup.defaults = {
+ corners: true,
+ shadow: true,
+ iconshadow: true,
+ inline: false,
+ wrapperEls: "span"
+};
+
+function closestEnabledButton( element ) {
+ while ( element ) {
+ var $ele = $( element );
+ if ( $ele.hasClass( "ui-btn" ) && !$ele.hasClass( "ui-disabled" ) ) {
+ break;
+ }
+ element = element.parentNode;
+ }
+ return element;
+}
+
+var attachEvents = function() {
+ $( document ).bind( {
+ "vmousedown": function( event ) {
+ var btn = closestEnabledButton( event.target ),
+ $btn, theme;
+
+ if ( btn ) {
+ $btn = $( btn );
+ theme = $btn.attr( "data-" + $.mobile.ns + "theme" );
+ $btn.removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-down-" + theme );
+ }
+ },
+ "vmousecancel vmouseup": function( event ) {
+ var btn = closestEnabledButton( event.target ),
+ $btn, theme;
+
+ if ( btn ) {
+ $btn = $( btn );
+ theme = $btn.attr( "data-" + $.mobile.ns + "theme" );
+ $btn.removeClass( "ui-btn-down-" + theme ).addClass( "ui-btn-up-" + theme );
+ }
+ },
+ "vmouseover focus": function( event ) {
+ var btn = closestEnabledButton( event.target ),
+ $btn, theme;
+
+ if ( btn ) {
+ $btn = $( btn );
+ theme = $btn.attr( "data-" + $.mobile.ns + "theme" );
+ $btn.removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-hover-" + theme );
+ }
+ },
+ "vmouseout blur": function( event ) {
+ var btn = closestEnabledButton( event.target ),
+ $btn, theme;
+
+ if ( btn ) {
+ $btn = $( btn );
+ theme = $btn.attr( "data-" + $.mobile.ns + "theme" );
+ $btn.removeClass( "ui-btn-hover-" + theme ).addClass( "ui-btn-up-" + theme );
+ }
+ }
+ });
+
+ attachEvents = null;
+};
+
+//links in bars, or those with data-role become buttons
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+
+ $( ":jqmData(role='button'), .ui-bar > a, .ui-header > a, .ui-footer > a, .ui-bar > :jqmData(role='controlgroup') > a", e.target )
+ .not( ".ui-btn, :jqmData(role='none'), :jqmData(role='nojs')" )
+ .buttonMarkup();
+});
+
+})( jQuery );
+/*
+* jQuery Mobile Framework: "controlgroup" plugin - corner-rounding for groups of buttons, checks, radios, etc
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+(function( $, undefined ) {
+
+$.fn.controlgroup = function( options ) {
+
+ return this.each(function() {
+
+ var $el = $( this ),
+ o = $.extend({
+ direction: $el.jqmData( "type" ) || "vertical",
+ shadow: false,
+ excludeInvisible: true
+ }, options ),
+ groupheading = $el.find( ">legend" ),
+ flCorners = o.direction == "horizontal" ? [ "ui-corner-left", "ui-corner-right" ] : [ "ui-corner-top", "ui-corner-bottom" ],
+ type = $el.find( "input:eq(0)" ).attr( "type" );
+
+ // Replace legend with more stylable replacement div
+ if ( groupheading.length ) {
+ $el.wrapInner( "<div class='ui-controlgroup-controls'></div>" );
+ $( "<div role='heading' class='ui-controlgroup-label'>" + groupheading.html() + "</div>" ).insertBefore( $el.children(0) );
+ groupheading.remove();
+ }
+
+ $el.addClass( "ui-corner-all ui-controlgroup ui-controlgroup-" + o.direction );
+
+ // TODO: This should be moved out to the closure
+ // otherwise it is redefined each time controlgroup() is called
+ function flipClasses( els ) {
+ els.removeClass( "ui-btn-corner-all ui-shadow" )
+ .eq( 0 ).addClass( flCorners[ 0 ] )
+ .end()
+ .filter( ":last" ).addClass( flCorners[ 1 ] ).addClass( "ui-controlgroup-last" );
+ }
+
+ flipClasses( $el.find( ".ui-btn" + ( o.excludeInvisible ? ":visible" : "" ) ) );
+ flipClasses( $el.find( ".ui-btn-inner" ) );
+
+ if ( o.shadow ) {
+ $el.addClass( "ui-shadow" );
+ }
+ });
+};
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+ $( ":jqmData(role='controlgroup')", e.target ).controlgroup({ excludeInvisible: false });
+});
+
+})(jQuery);/*
+* jQuery Mobile Framework : "links" plugin - simple class additions for links
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+
+(function( $, undefined ) {
+
+$( document ).bind( "pagecreate create", function( e ){
+
+ //links within content areas
+ $( e.target )
+ .find( "a" )
+ .not( ".ui-btn, .ui-link-inherit, :jqmData(role='none'), :jqmData(role='nojs')" )
+ .addClass( "ui-link" );
+
+});
+
+})( jQuery );/*
+* jQuery Mobile Framework : "fixHeaderFooter" plugin - on-demand positioning for headers,footers
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+
+(function( $, undefined ) {
+
+var slideDownClass = "ui-header-fixed ui-fixed-inline fade",
+ slideUpClass = "ui-footer-fixed ui-fixed-inline fade",
+
+ slideDownSelector = ".ui-header:jqmData(position='fixed')",
+ slideUpSelector = ".ui-footer:jqmData(position='fixed')";
+
+$.fn.fixHeaderFooter = function( options ) {
+
+ if ( !$.support.scrollTop || ( $.support.touchOverflow && $.mobile.touchOverflowEnabled ) ) {
+ return this;
+ }
+
+ return this.each(function() {
+ var $this = $( this );
+
+ if ( $this.jqmData( "fullscreen" ) ) {
+ $this.addClass( "ui-page-fullscreen" );
+ }
+
+ // Should be slidedown
+ $this.find( slideDownSelector ).addClass( slideDownClass );
+
+ // Should be slideup
+ $this.find( slideUpSelector ).addClass( slideUpClass );
+ });
+};
+
+// single controller for all showing,hiding,toggling
+$.mobile.fixedToolbars = (function() {
+
+ if ( !$.support.scrollTop || ( $.support.touchOverflow && $.mobile.touchOverflowEnabled ) ) {
+ return;
+ }
+
+ var stickyFooter, delayTimer,
+ currentstate = "inline",
+ autoHideMode = false,
+ showDelay = 100,
+ ignoreTargets = "a,input,textarea,select,button,label,.ui-header-fixed,.ui-footer-fixed",
+ toolbarSelector = ".ui-header-fixed:first, .ui-footer-fixed:not(.ui-footer-duplicate):last",
+ // for storing quick references to duplicate footers
+ supportTouch = $.support.touch,
+ touchStartEvent = supportTouch ? "touchstart" : "mousedown",
+ touchStopEvent = supportTouch ? "touchend" : "mouseup",
+ stateBefore = null,
+ scrollTriggered = false,
+ touchToggleEnabled = true;
+
+ function showEventCallback( event ) {
+ // An event that affects the dimensions of the visual viewport has
+ // been triggered. If the header and/or footer for the current page are in overlay
+ // mode, we want to hide them, and then fire off a timer to show them at a later
+ // point. Events like a resize can be triggered continuously during a scroll, on
+ // some platforms, so the timer is used to delay the actual positioning until the
+ // flood of events have subsided.
+ //
+ // If we are in autoHideMode, we don't do anything because we know the scroll
+ // callbacks for the plugin will fire off a show when the scrolling has stopped.
+ if ( !autoHideMode && currentstate === "overlay" ) {
+ if ( !delayTimer ) {
+ $.mobile.fixedToolbars.hide( true );
+ }
+
+ $.mobile.fixedToolbars.startShowTimer();
+ }
+ }
+
+ $(function() {
+ var $document = $( document ),
+ $window = $( window );
+
+ $document
+ .bind( "vmousedown", function( event ) {
+ if ( touchToggleEnabled ) {
+ stateBefore = currentstate;
+ }
+ })
+ .bind( "vclick", function( event ) {
+ if ( touchToggleEnabled ) {
+
+ if ( $(event.target).closest( ignoreTargets ).length ) {
+ return;
+ }
+
+ if ( !scrollTriggered ) {
+ $.mobile.fixedToolbars.toggle( stateBefore );
+ stateBefore = null;
+ }
+ }
+ })
+ .bind( "silentscroll", showEventCallback );
+
+
+ // The below checks first for a $(document).scrollTop() value, and if zero, binds scroll events to $(window) instead.
+ // If the scrollTop value is actually zero, both will return zero anyway.
+ //
+ // Works with $(document), not $(window) : Opera Mobile (WinMO phone; kinda broken anyway)
+ // Works with $(window), not $(document) : IE 7/8
+ // Works with either $(window) or $(document) : Chrome, FF 3.6/4, Android 1.6/2.1, iOS
+ // Needs work either way : BB5, Opera Mobile (iOS)
+
+ ( ( $document.scrollTop() === 0 ) ? $window : $document )
+ .bind( "scrollstart", function( event ) {
+
+ scrollTriggered = true;
+
+ if ( stateBefore === null ) {
+ stateBefore = currentstate;
+ }
+
+ // We only enter autoHideMode if the headers/footers are in
+ // an overlay state or the show timer was started. If the
+ // show timer is set, clear it so the headers/footers don't
+ // show up until after we're done scrolling.
+ var isOverlayState = stateBefore == "overlay";
+
+ autoHideMode = isOverlayState || !!delayTimer;
+
+ if ( autoHideMode ) {
+ $.mobile.fixedToolbars.clearShowTimer();
+
+ if ( isOverlayState ) {
+ $.mobile.fixedToolbars.hide( true );
+ }
+ }
+ })
+ .bind( "scrollstop", function( event ) {
+
+ if ( $( event.target ).closest( ignoreTargets ).length ) {
+ return;
+ }
+
+ scrollTriggered = false;
+
+ if ( autoHideMode ) {
+ $.mobile.fixedToolbars.startShowTimer();
+ autoHideMode = false;
+ }
+ stateBefore = null;
+ });
+
+ $window.bind( "resize", showEventCallback );
+ });
+
+ // 1. Before page is shown, check for duplicate footer
+ // 2. After page is shown, append footer to new page
+ $( ".ui-page" )
+ .live( "pagebeforeshow", function( event, ui ) {
+
+ var page = $( event.target ),
+ footer = page.find( ":jqmData(role='footer')" ),
+ id = footer.data( "id" ),
+ prevPage = ui.prevPage,
+ prevFooter = prevPage && prevPage.find( ":jqmData(role='footer')" ),
+ prevFooterMatches = prevFooter.length && prevFooter.jqmData( "id" ) === id;
+
+ if ( id && prevFooterMatches ) {
+ stickyFooter = footer;
+ setTop( stickyFooter.removeClass( "fade in out" ).appendTo( $.mobile.pageContainer ) );
+ }
+ })
+ .live( "pageshow", function( event, ui ) {
+
+ var $this = $( this );
+
+ if ( stickyFooter && stickyFooter.length ) {
+
+ setTimeout(function() {
+ setTop( stickyFooter.appendTo( $this ).addClass( "fade" ) );
+ stickyFooter = null;
+ }, 500);
+ }
+
+ $.mobile.fixedToolbars.show( true, this );
+ });
+
+ // When a collapsiable is hidden or shown we need to trigger the fixed toolbar to reposition itself (#1635)
+ $( ".ui-collapsible-contain" ).live( "collapse expand", showEventCallback );
+
+ // element.getBoundingClientRect() is broken in iOS 3.2.1 on the iPad. The
+ // coordinates inside of the rect it returns don't have the page scroll position
+ // factored out of it like the other platforms do. To get around this,
+ // we'll just calculate the top offset the old fashioned way until core has
+ // a chance to figure out how to handle this situation.
+ //
+ // TODO: We'll need to get rid of getOffsetTop() once a fix gets folded into core.
+
+ function getOffsetTop( ele ) {
+ var top = 0,
+ op, body;
+
+ if ( ele ) {
+ body = document.body;
+ op = ele.offsetParent;
+ top = ele.offsetTop;
+
+ while ( ele && ele != body ) {
+ top += ele.scrollTop || 0;
+
+ if ( ele == op ) {
+ top += op.offsetTop;
+ op = ele.offsetParent;
+ }
+
+ ele = ele.parentNode;
+ }
+ }
+ return top;
+ }
+
+ function setTop( el ) {
+ var fromTop = $(window).scrollTop(),
+ thisTop = getOffsetTop( el[ 0 ] ), // el.offset().top returns the wrong value on iPad iOS 3.2.1, call our workaround instead.
+ thisCSStop = el.css( "top" ) == "auto" ? 0 : parseFloat(el.css( "top" )),
+ screenHeight = window.innerHeight,
+ thisHeight = el.outerHeight(),
+ useRelative = el.parents( ".ui-page:not(.ui-page-fullscreen)" ).length,
+ relval;
+
+ if ( el.is( ".ui-header-fixed" ) ) {
+
+ relval = fromTop - thisTop + thisCSStop;
+
+ if ( relval < thisTop ) {
+ relval = 0;
+ }
+
+ return el.css( "top", useRelative ? relval : fromTop );
+ } else {
+ // relval = -1 * (thisTop - (fromTop + screenHeight) + thisCSStop + thisHeight);
+ // if ( relval > thisTop ) { relval = 0; }
+ relval = fromTop + screenHeight - thisHeight - (thisTop - thisCSStop );
+
+ return el.css( "top", useRelative ? relval : fromTop + screenHeight - thisHeight );
+ }
+ }
+
+ // Exposed methods
+ return {
+
+ show: function( immediately, page ) {
+
+ $.mobile.fixedToolbars.clearShowTimer();
+
+ currentstate = "overlay";
+
+ var $ap = page ? $( page ) :
+ ( $.mobile.activePage ? $.mobile.activePage :
+ $( ".ui-page-active" ) );
+
+ return $ap.children( toolbarSelector ).each(function() {
+
+ var el = $( this ),
+ fromTop = $( window ).scrollTop(),
+ // el.offset().top returns the wrong value on iPad iOS 3.2.1, call our workaround instead.
+ thisTop = getOffsetTop( el[ 0 ] ),
+ screenHeight = window.innerHeight,
+ thisHeight = el.outerHeight(),
+ alreadyVisible = ( el.is( ".ui-header-fixed" ) && fromTop <= thisTop + thisHeight ) ||
+ ( el.is( ".ui-footer-fixed" ) && thisTop <= fromTop + screenHeight );
+
+ // Add state class
+ el.addClass( "ui-fixed-overlay" ).removeClass( "ui-fixed-inline" );
+
+ if ( !alreadyVisible && !immediately ) {
+ el.animationComplete(function() {
+ el.removeClass( "in" );
+ }).addClass( "in" );
+ }
+ setTop(el);
+ });
+ },
+
+ hide: function( immediately ) {
+
+ currentstate = "inline";
+
+ var $ap = $.mobile.activePage ? $.mobile.activePage :
+ $( ".ui-page-active" );
+
+ return $ap.children( toolbarSelector ).each(function() {
+
+ var el = $(this),
+ thisCSStop = el.css( "top" ),
+ classes;
+
+ thisCSStop = thisCSStop == "auto" ? 0 :
+ parseFloat(thisCSStop);
+
+ // Add state class
+ el.addClass( "ui-fixed-inline" ).removeClass( "ui-fixed-overlay" );
+
+ if ( thisCSStop < 0 || ( el.is( ".ui-header-fixed" ) && thisCSStop !== 0 ) ) {
+
+ if ( immediately ) {
+ el.css( "top", 0);
+ } else {
+
+ if ( el.css( "top" ) !== "auto" && parseFloat( el.css( "top" ) ) !== 0 ) {
+
+ classes = "out reverse";
+
+ el.animationComplete(function() {
+ el.removeClass( classes ).css( "top", 0 );
+ }).addClass( classes );
+ }
+ }
+ }
+ });
+ },
+
+ startShowTimer: function() {
+
+ $.mobile.fixedToolbars.clearShowTimer();
+
+ var args = [].slice.call( arguments );
+
+ delayTimer = setTimeout(function() {
+ delayTimer = undefined;
+ $.mobile.fixedToolbars.show.apply( null, args );
+ }, showDelay);
+ },
+
+ clearShowTimer: function() {
+ if ( delayTimer ) {
+ clearTimeout( delayTimer );
+ }
+ delayTimer = undefined;
+ },
+
+ toggle: function( from ) {
+ if ( from ) {
+ currentstate = from;
+ }
+ return ( currentstate === "overlay" ) ? $.mobile.fixedToolbars.hide() :
+ $.mobile.fixedToolbars.show();
+ },
+
+ setTouchToggleEnabled: function( enabled ) {
+ touchToggleEnabled = enabled;
+ }
+ };
+})();
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( event ) {
+
+ if ( $( ":jqmData(position='fixed')", event.target ).length ) {
+
+ $( event.target ).each(function() {
+
+ if ( !$.support.scrollTop || ( $.support.touchOverflow && $.mobile.touchOverflowEnabled ) ) {
+ return this;
+ }
+
+ var $this = $( this );
+
+ if ( $this.jqmData( "fullscreen" ) ) {
+ $this.addClass( "ui-page-fullscreen" );
+ }
+
+ // Should be slidedown
+ $this.find( slideDownSelector ).addClass( slideDownClass );
+
+ // Should be slideup
+ $this.find( slideUpSelector ).addClass( slideUpClass );
+
+ })
+
+ }
+});
+
+})( jQuery );
+/*
+* jQuery Mobile Framework : "fixHeaderFooter" native plugin - Behavior for "fixed" headers,footers, and scrolling inner content
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+
+(function( $, undefined ) {
+
+// Enable touch overflow scrolling when it's natively supported
+$.mobile.touchOverflowEnabled = false;
+
+// Enabled zoom when touch overflow is enabled. Can cause usability issues, unfortunately
+$.mobile.touchOverflowZoomEnabled = false;
+
+$( document ).bind( "pagecreate", function( event ) {
+ if( $.support.touchOverflow && $.mobile.touchOverflowEnabled ){
+
+ var $target = $( event.target ),
+ scrollStartY = 0;
+
+ if( $target.is( ":jqmData(role='page')" ) ){
+
+ $target.each(function() {
+ var $page = $( this ),
+ $fixies = $page.find( ":jqmData(role='header'), :jqmData(role='footer')" ).filter( ":jqmData(position='fixed')" ),
+ fullScreen = $page.jqmData( "fullscreen" ),
+ $scrollElem = $fixies.length ? $page.find( ".ui-content" ) : $page;
+
+ $page.addClass( "ui-mobile-touch-overflow" );
+
+ $scrollElem.bind( "scrollstop", function(){
+ if( $scrollElem.scrollTop() > 0 ){
+ window.scrollTo( 0, $.mobile.defaultHomeScroll );
+ }
+ });
+
+ if( $fixies.length ){
+
+ $page.addClass( "ui-native-fixed" );
+
+ if( fullScreen ){
+
+ $page.addClass( "ui-native-fullscreen" );
+
+ $fixies.addClass( "fade in" );
+
+ $( document ).bind( "vclick", function(){
+ $fixies
+ .removeClass( "ui-native-bars-hidden" )
+ .toggleClass( "in out" )
+ .animationComplete(function(){
+ $(this).not( ".in" ).addClass( "ui-native-bars-hidden" );
+ });
+ });
+ }
+ }
+ });
+ }
+ }
+});
+
+})( jQuery );
+/*!
+ * jQuery Mobile v@VERSION
+ * http://jquerymobile.com/
+ *
+ * Copyright 2010, jQuery Project
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ */
+
+(function( $, window, undefined ) {
+ var $html = $( "html" ),
+ $head = $( "head" ),
+ $window = $( window );
+
+ // trigger mobileinit event - useful hook for configuring $.mobile settings before they're used
+ $( window.document ).trigger( "mobileinit" );
+
+ // support conditions
+ // if device support condition(s) aren't met, leave things as they are -> a basic, usable experience,
+ // otherwise, proceed with the enhancements
+ if ( !$.mobile.gradeA() ) {
+ return;
+ }
+
+ // override ajaxEnabled on platforms that have known conflicts with hash history updates
+ // or generally work better browsing in regular http for full page refreshes (BB5, Opera Mini)
+ if ( $.mobile.ajaxBlacklist ) {
+ $.mobile.ajaxEnabled = false;
+ }
+
+ // add mobile, initial load "rendering" classes to docEl
+ $html.addClass( "ui-mobile ui-mobile-rendering" );
+
+ // loading div which appears during Ajax requests
+ // will not appear if $.mobile.loadingMessage is false
+ var $loader = $( "<div class='ui-loader ui-body-a ui-corner-all'><span class='ui-icon ui-icon-loading spin'></span><h1></h1></div>" );
+
+ $.extend($.mobile, {
+ // turn on/off page loading message.
+ showPageLoadingMsg: function() {
+ if ( $.mobile.loadingMessage ) {
+ var activeBtn = $( "." + $.mobile.activeBtnClass ).first();
+
+ $loader
+ .find( "h1" )
+ .text( $.mobile.loadingMessage )
+ .end()
+ .appendTo( $.mobile.pageContainer )
+ // position at y center (if scrollTop supported), above the activeBtn (if defined), or just 100px from top
+ .css({
+ top: $.support.scrollTop && $window.scrollTop() + $window.height() / 2 ||
+ activeBtn.length && activeBtn.offset().top || 100
+ });
+ }
+
+ $html.addClass( "ui-loading" );
+ },
+
+ hidePageLoadingMsg: function() {
+ $html.removeClass( "ui-loading" );
+ },
+
+ // find and enhance the pages in the dom and transition to the first page.
+ initializePage: function() {
+ // find present pages
+ var $pages = $( ":jqmData(role='page')" );
+
+ // if no pages are found, create one with body's inner html
+ if ( !$pages.length ) {
+ $pages = $( "body" ).wrapInner( "<div data-" + $.mobile.ns + "role='page'></div>" ).children( 0 );
+ }
+
+ // add dialogs, set data-url attrs
+ $pages.add( ":jqmData(role='dialog')" ).each(function() {
+ var $this = $(this);
+
+ // unless the data url is already set set it to the pathname
+ if ( !$this.jqmData("url") ) {
+ $this.attr( "data-" + $.mobile.ns + "url", $this.attr( "id" ) || location.pathname + location.search );
+ }
+ });
+
+ // define first page in dom case one backs out to the directory root (not always the first page visited, but defined as fallback)
+ $.mobile.firstPage = $pages.first();
+
+ // define page container
+ $.mobile.pageContainer = $pages.first().parent().addClass( "ui-mobile-viewport" );
+
+ // alert listeners that the pagecontainer has been determined for binding
+ // to events triggered on it
+ $window.trigger( "pagecontainercreate" );
+
+ // cue page loading message
+ $.mobile.showPageLoadingMsg();
+
+ // if hashchange listening is disabled or there's no hash deeplink, change to the first page in the DOM
+ if ( !$.mobile.hashListeningEnabled || !$.mobile.path.stripHash( location.hash ) ) {
+ $.mobile.changePage( $.mobile.firstPage, { transition: "none", reverse: true, changeHash: false, fromHashChange: true } );
+ }
+ // otherwise, trigger a hashchange to load a deeplink
+ else {
+ $window.trigger( "hashchange", [ true ] );
+ }
+ }
+ });
+
+ // This function injects a meta viewport tag to prevent scaling. Off by default, on by default when touchOverflow scrolling is enabled
+ function disableZoom() {
+ var cont = "user-scalable=no",
+ meta = $( "meta[name='viewport']" );
+
+ if( meta.length ){
+ meta.attr( "content", meta.attr( "content" ) + ", " + cont );
+ }
+ else{
+ $( "head" ).prepend( "<meta>", { "name": "viewport", "content": cont } );
+ }
+ }
+
+ // if touch-overflow is enabled, disable user scaling, as it creates usability issues
+ if( $.support.touchOverflow && $.mobile.touchOverflowEnabled && !$.mobile.touchOverflowZoomEnabled ){
+ disableZoom();
+ }
+
+ // initialize events now, after mobileinit has occurred
+ $.mobile._registerInternalEvents();
+
+ // check which scrollTop value should be used by scrolling to 1 immediately at domready
+ // then check what the scroll top is. Android will report 0... others 1
+ // note that this initial scroll won't hide the address bar. It's just for the check.
+ $(function() {
+ window.scrollTo( 0, 1 );
+
+ // if defaultHomeScroll hasn't been set yet, see if scrollTop is 1
+ // it should be 1 in most browsers, but android treats 1 as 0 (for hiding addr bar)
+ // so if it's 1, use 0 from now on
+ $.mobile.defaultHomeScroll = ( !$.support.scrollTop || $(window).scrollTop() === 1 ) ? 0 : 1;
+
+ //dom-ready inits
+ if( $.mobile.autoInitializePage ){
+ $.mobile.initializePage();
+ }
+
+ // window load event
+ // hide iOS browser chrome on load
+ $window.load( $.mobile.silentScroll );
+ });
+})( jQuery, this );
+
--- a/labs/stop.pdf.php
+++ b/labs/stop.pdf.php
@@ -34,7 +34,7 @@
$stop[2],
$stop[3]
)
- ), 0, "iconb", false) . "</td></tr>";
+ ), 0, false) . "</td></tr>";
$url = $APIurl . "/json/stoptrips?stop=" . $stopid . "&time=" . midnight_seconds() . "&service_period=" . service_period();
$trips = json_decode(getPage($url));
$html.= "</table><br><br><table>";
--- a/lib/autocomplete.php
+++ b/lib/autocomplete.php
@@ -3,7 +3,7 @@
$result = Array();
if (isset($_REQUEST['term'])) {
$term = filter_var($_REQUEST['term'], FILTER_SANITIZE_STRING);
- $query = "Select stop_name,min(stop_lat) as stop_lat,min(stop_lon) as stop_lon from stops where stop_name LIKE :term group by stop_name";
+ $query = "Select stop_name,min(stop_lat) as stop_lat,min(stop_lon) as stop_lon from stops where stop_name ILIKE :term group by stop_name";
$query = $conn->prepare($query);
$term = "$term%";
$query->bindParam(":term", $term);
--- a/lib/openid-php/Auth/OpenID.php
+++ /dev/null
@@ -1,564 +1,1 @@
-<?php
-/**
- * This is the PHP OpenID library by JanRain, Inc.
- *
- * This module contains core utility functionality used by the
- * library. See Consumer.php and Server.php for the consumer and
- * server implementations.
- *
- * PHP versions 4 and 5
- *
- * LICENSE: See the COPYING file included in this distribution.
- *
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-
-/**
- * The library version string
- */
-define('Auth_OpenID_VERSION', '2.2.2');
-
-/**
- * Require the fetcher code.
- */
-require_once "Auth/Yadis/PlainHTTPFetcher.php";
-require_once "Auth/Yadis/ParanoidHTTPFetcher.php";
-require_once "Auth/OpenID/BigMath.php";
-require_once "Auth/OpenID/URINorm.php";
-
-/**
- * Status code returned by the server when the only option is to show
- * an error page, since we do not have enough information to redirect
- * back to the consumer. The associated value is an error message that
- * should be displayed on an HTML error page.
- *
- * @see Auth_OpenID_Server
- */
-define('Auth_OpenID_LOCAL_ERROR', 'local_error');
-
-/**
- * Status code returned when there is an error to return in key-value
- * form to the consumer. The caller should return a 400 Bad Request
- * response with content-type text/plain and the value as the body.
- *
- * @see Auth_OpenID_Server
- */
-define('Auth_OpenID_REMOTE_ERROR', 'remote_error');
-
-/**
- * Status code returned when there is a key-value form OK response to
- * the consumer. The value associated with this code is the
- * response. The caller should return a 200 OK response with
- * content-type text/plain and the value as the body.
- *
- * @see Auth_OpenID_Server
- */
-define('Auth_OpenID_REMOTE_OK', 'remote_ok');
-
-/**
- * Status code returned when there is a redirect back to the
- * consumer. The value is the URL to redirect back to. The caller
- * should return a 302 Found redirect with a Location: header
- * containing the URL.
- *
- * @see Auth_OpenID_Server
- */
-define('Auth_OpenID_REDIRECT', 'redirect');
-
-/**
- * Status code returned when the caller needs to authenticate the
- * user. The associated value is a {@link Auth_OpenID_ServerRequest}
- * object that can be used to complete the authentication. If the user
- * has taken some authentication action, use the retry() method of the
- * {@link Auth_OpenID_ServerRequest} object to complete the request.
- *
- * @see Auth_OpenID_Server
- */
-define('Auth_OpenID_DO_AUTH', 'do_auth');
-
-/**
- * Status code returned when there were no OpenID arguments
- * passed. This code indicates that the caller should return a 200 OK
- * response and display an HTML page that says that this is an OpenID
- * server endpoint.
- *
- * @see Auth_OpenID_Server
- */
-define('Auth_OpenID_DO_ABOUT', 'do_about');
-
-/**
- * Defines for regexes and format checking.
- */
-define('Auth_OpenID_letters',
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
-
-define('Auth_OpenID_digits',
- "0123456789");
-
-define('Auth_OpenID_punct',
- "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~");
-
-Auth_OpenID_include_init();
-
-/**
- * The OpenID utility function class.
- *
- * @package OpenID
- * @access private
- */
-class Auth_OpenID {
-
- /**
- * Return true if $thing is an Auth_OpenID_FailureResponse object;
- * false if not.
- *
- * @access private
- */
- static function isFailure($thing)
- {
- return is_a($thing, 'Auth_OpenID_FailureResponse');
- }
-
- /**
- * Gets the query data from the server environment based on the
- * request method used. If GET was used, this looks at
- * $_SERVER['QUERY_STRING'] directly. If POST was used, this
- * fetches data from the special php://input file stream.
- *
- * Returns an associative array of the query arguments.
- *
- * Skips invalid key/value pairs (i.e. keys with no '=value'
- * portion).
- *
- * Returns an empty array if neither GET nor POST was used, or if
- * POST was used but php://input cannot be opened.
- *
- * See background:
- * http://lists.openidenabled.com/pipermail/dev/2007-March/000395.html
- *
- * @access private
- */
- static function getQuery($query_str=null)
- {
- $data = array();
-
- if ($query_str !== null) {
- $data = Auth_OpenID::params_from_string($query_str);
- } else if (!array_key_exists('REQUEST_METHOD', $_SERVER)) {
- // Do nothing.
- } else {
- // XXX HACK FIXME HORRIBLE.
- //
- // POSTing to a URL with query parameters is acceptable, but
- // we don't have a clean way to distinguish those parameters
- // when we need to do things like return_to verification
- // which only want to look at one kind of parameter. We're
- // going to emulate the behavior of some other environments
- // by defaulting to GET and overwriting with POST if POST
- // data is available.
- $data = Auth_OpenID::params_from_string($_SERVER['QUERY_STRING']);
-
- if ($_SERVER['REQUEST_METHOD'] == 'POST') {
- $str = file_get_contents('php://input');
-
- if ($str === false) {
- $post = array();
- } else {
- $post = Auth_OpenID::params_from_string($str);
- }
-
- $data = array_merge($data, $post);
- }
- }
-
- return $data;
- }
-
- static function params_from_string($str)
- {
- $chunks = explode("&", $str);
-
- $data = array();
- foreach ($chunks as $chunk) {
- $parts = explode("=", $chunk, 2);
-
- if (count($parts) != 2) {
- continue;
- }
-
- list($k, $v) = $parts;
- $data[urldecode($k)] = urldecode($v);
- }
-
- return $data;
- }
-
- /**
- * Create dir_name as a directory if it does not exist. If it
- * exists, make sure that it is, in fact, a directory. Returns
- * true if the operation succeeded; false if not.
- *
- * @access private
- */
- static function ensureDir($dir_name)
- {
- if (is_dir($dir_name) || @mkdir($dir_name)) {
- return true;
- } else {
- $parent_dir = dirname($dir_name);
-
- // Terminal case; there is no parent directory to create.
- if ($parent_dir == $dir_name) {
- return true;
- }
-
- return (Auth_OpenID::ensureDir($parent_dir) && @mkdir($dir_name));
- }
- }
-
- /**
- * Adds a string prefix to all values of an array. Returns a new
- * array containing the prefixed values.
- *
- * @access private
- */
- static function addPrefix($values, $prefix)
- {
- $new_values = array();
- foreach ($values as $s) {
- $new_values[] = $prefix . $s;
- }
- return $new_values;
- }
-
- /**
- * Convenience function for getting array values. Given an array
- * $arr and a key $key, get the corresponding value from the array
- * or return $default if the key is absent.
- *
- * @access private
- */
- static function arrayGet($arr, $key, $fallback = null)
- {
- if (is_array($arr)) {
- if (array_key_exists($key, $arr)) {
- return $arr[$key];
- } else {
- return $fallback;
- }
- } else {
- trigger_error("Auth_OpenID::arrayGet (key = ".$key.") expected " .
- "array as first parameter, got " .
- gettype($arr), E_USER_WARNING);
-
- return false;
- }
- }
-
- /**
- * Replacement for PHP's broken parse_str.
- */
- static function parse_str($query)
- {
- if ($query === null) {
- return null;
- }
-
- $parts = explode('&', $query);
-
- $new_parts = array();
- for ($i = 0; $i < count($parts); $i++) {
- $pair = explode('=', $parts[$i]);
-
- if (count($pair) != 2) {
- continue;
- }
-
- list($key, $value) = $pair;
- $new_parts[urldecode($key)] = urldecode($value);
- }
-
- return $new_parts;
- }
-
- /**
- * Implements the PHP 5 'http_build_query' functionality.
- *
- * @access private
- * @param array $data Either an array key/value pairs or an array
- * of arrays, each of which holding two values: a key and a value,
- * sequentially.
- * @return string $result The result of url-encoding the key/value
- * pairs from $data into a URL query string
- * (e.g. "username=bob&id=56").
- */
- static function httpBuildQuery($data)
- {
- $pairs = array();
- foreach ($data as $key => $value) {
- if (is_array($value)) {
- $pairs[] = urlencode($value[0])."=".urlencode($value[1]);
- } else {
- $pairs[] = urlencode($key)."=".urlencode($value);
- }
- }
- return implode("&", $pairs);
- }
-
- /**
- * "Appends" query arguments onto a URL. The URL may or may not
- * already have arguments (following a question mark).
- *
- * @access private
- * @param string $url A URL, which may or may not already have
- * arguments.
- * @param array $args Either an array key/value pairs or an array of
- * arrays, each of which holding two values: a key and a value,
- * sequentially. If $args is an ordinary key/value array, the
- * parameters will be added to the URL in sorted alphabetical order;
- * if $args is an array of arrays, their order will be preserved.
- * @return string $url The original URL with the new parameters added.
- *
- */
- static function appendArgs($url, $args)
- {
- if (count($args) == 0) {
- return $url;
- }
-
- // Non-empty array; if it is an array of arrays, use
- // multisort; otherwise use sort.
- if (array_key_exists(0, $args) &&
- is_array($args[0])) {
- // Do nothing here.
- } else {
- $keys = array_keys($args);
- sort($keys);
- $new_args = array();
- foreach ($keys as $key) {
- $new_args[] = array($key, $args[$key]);
- }
- $args = $new_args;
- }
-
- $sep = '?';
- if (strpos($url, '?') !== false) {
- $sep = '&';
- }
-
- return $url . $sep . Auth_OpenID::httpBuildQuery($args);
- }
-
- /**
- * Implements python's urlunparse, which is not available in PHP.
- * Given the specified components of a URL, this function rebuilds
- * and returns the URL.
- *
- * @access private
- * @param string $scheme The scheme (e.g. 'http'). Defaults to 'http'.
- * @param string $host The host. Required.
- * @param string $port The port.
- * @param string $path The path.
- * @param string $query The query.
- * @param string $fragment The fragment.
- * @return string $url The URL resulting from assembling the
- * specified components.
- */
- static function urlunparse($scheme, $host, $port = null, $path = '/',
- $query = '', $fragment = '')
- {
-
- if (!$scheme) {
- $scheme = 'http';
- }
-
- if (!$host) {
- return false;
- }
-
- if (!$path) {
- $path = '';
- }
-
- $result = $scheme . "://" . $host;
-
- if ($port) {
- $result .= ":" . $port;
- }
-
- $result .= $path;
-
- if ($query) {
- $result .= "?" . $query;
- }
-
- if ($fragment) {
- $result .= "#" . $fragment;
- }
-
- return $result;
- }
-
- /**
- * Given a URL, this "normalizes" it by adding a trailing slash
- * and / or a leading http:// scheme where necessary. Returns
- * null if the original URL is malformed and cannot be normalized.
- *
- * @access private
- * @param string $url The URL to be normalized.
- * @return mixed $new_url The URL after normalization, or null if
- * $url was malformed.
- */
- static function normalizeUrl($url)
- {
- @$parsed = parse_url($url);
-
- if (!$parsed) {
- return null;
- }
-
- if (isset($parsed['scheme']) &&
- isset($parsed['host'])) {
- $scheme = strtolower($parsed['scheme']);
- if (!in_array($scheme, array('http', 'https'))) {
- return null;
- }
- } else {
- $url = 'http://' . $url;
- }
-
- $normalized = Auth_OpenID_urinorm($url);
- if ($normalized === null) {
- return null;
- }
- list($defragged, $frag) = Auth_OpenID::urldefrag($normalized);
- return $defragged;
- }
-
- /**
- * Replacement (wrapper) for PHP's intval() because it's broken.
- *
- * @access private
- */
- static function intval($value)
- {
- $re = "/^\\d+$/";
-
- if (!preg_match($re, $value)) {
- return false;
- }
-
- return intval($value);
- }
-
- /**
- * Count the number of bytes in a string independently of
- * multibyte support conditions.
- *
- * @param string $str The string of bytes to count.
- * @return int The number of bytes in $str.
- */
- static function bytes($str)
- {
- return strlen(bin2hex($str)) / 2;
- }
-
- /**
- * Get the bytes in a string independently of multibyte support
- * conditions.
- */
- static function toBytes($str)
- {
- $hex = bin2hex($str);
-
- if (!$hex) {
- return array();
- }
-
- $b = array();
- for ($i = 0; $i < strlen($hex); $i += 2) {
- $b[] = chr(base_convert(substr($hex, $i, 2), 16, 10));
- }
-
- return $b;
- }
-
- static function urldefrag($url)
- {
- $parts = explode("#", $url, 2);
-
- if (count($parts) == 1) {
- return array($parts[0], "");
- } else {
- return $parts;
- }
- }
-
- static function filter($callback, &$sequence)
- {
- $result = array();
-
- foreach ($sequence as $item) {
- if (call_user_func_array($callback, array($item))) {
- $result[] = $item;
- }
- }
-
- return $result;
- }
-
- static function update(&$dest, &$src)
- {
- foreach ($src as $k => $v) {
- $dest[$k] = $v;
- }
- }
-
- /**
- * Wrap PHP's standard error_log functionality. Use this to
- * perform all logging. It will interpolate any additional
- * arguments into the format string before logging.
- *
- * @param string $format_string The sprintf format for the message
- */
- static function log($format_string)
- {
- $args = func_get_args();
- $message = call_user_func_array('sprintf', $args);
- error_log($message);
- }
-
- static function autoSubmitHTML($form, $title="OpenId transaction in progress")
- {
- return("<html>".
- "<head><title>".
- $title .
- "</title></head>".
- "<body onload='document.forms[0].submit();'>".
- $form .
- "<script>".
- "var elements = document.forms[0].elements;".
- "for (var i = 0; i < elements.length; i++) {".
- " elements[i].style.display = \"none\";".
- "}".
- "</script>".
- "</body>".
- "</html>");
- }
-}
-
-/*
- * Function to run when this file is included.
- * Abstracted to a function to make life easier
- * for some PHP optimizers.
- */
-function Auth_OpenID_include_init() {
- if (Auth_OpenID_getMathLib() === null) {
- Auth_OpenID_setNoMathSupport();
- }
-}
-
--- a/lib/openid-php/Auth/OpenID/AX.php
+++ /dev/null
@@ -1,1023 +1,1 @@
-<?php
-/**
- * Implements the OpenID attribute exchange specification, version 1.0
- * as of svn revision 370 from openid.net svn.
- *
- * @package OpenID
- */
-
-/**
- * Require utility classes and functions for the consumer.
- */
-require_once "Auth/OpenID/Extension.php";
-require_once "Auth/OpenID/Message.php";
-require_once "Auth/OpenID/TrustRoot.php";
-
-define('Auth_OpenID_AX_NS_URI',
- 'http://openid.net/srv/ax/1.0');
-
-// Use this as the 'count' value for an attribute in a FetchRequest to
-// ask for as many values as the OP can provide.
-define('Auth_OpenID_AX_UNLIMITED_VALUES', 'unlimited');
-
-// Minimum supported alias length in characters. Here for
-// completeness.
-define('Auth_OpenID_AX_MINIMUM_SUPPORTED_ALIAS_LENGTH', 32);
-
-/**
- * AX utility class.
- *
- * @package OpenID
- */
-class Auth_OpenID_AX {
- /**
- * @param mixed $thing Any object which may be an
- * Auth_OpenID_AX_Error object.
- *
- * @return bool true if $thing is an Auth_OpenID_AX_Error; false
- * if not.
- */
- static function isError($thing)
- {
- return is_a($thing, 'Auth_OpenID_AX_Error');
- }
-}
-
-/**
- * Check an alias for invalid characters; raise AXError if any are
- * found. Return None if the alias is valid.
- */
-function Auth_OpenID_AX_checkAlias($alias)
-{
- if (strpos($alias, ',') !== false) {
- return new Auth_OpenID_AX_Error(sprintf(
- "Alias %s must not contain comma", $alias));
- }
- if (strpos($alias, '.') !== false) {
- return new Auth_OpenID_AX_Error(sprintf(
- "Alias %s must not contain period", $alias));
- }
-
- return true;
-}
-
-/**
- * Results from data that does not meet the attribute exchange 1.0
- * specification
- *
- * @package OpenID
- */
-class Auth_OpenID_AX_Error {
- function Auth_OpenID_AX_Error($message=null)
- {
- $this->message = $message;
- }
-}
-
-/**
- * Abstract class containing common code for attribute exchange
- * messages.
- *
- * @package OpenID
- */
-class Auth_OpenID_AX_Message extends Auth_OpenID_Extension {
- /**
- * ns_alias: The preferred namespace alias for attribute exchange
- * messages
- */
- var $ns_alias = 'ax';
-
- /**
- * mode: The type of this attribute exchange message. This must be
- * overridden in subclasses.
- */
- var $mode = null;
-
- var $ns_uri = Auth_OpenID_AX_NS_URI;
-
- /**
- * Return Auth_OpenID_AX_Error if the mode in the attribute
- * exchange arguments does not match what is expected for this
- * class; true otherwise.
- *
- * @access private
- */
- function _checkMode($ax_args)
- {
- $mode = Auth_OpenID::arrayGet($ax_args, 'mode');
- if ($mode != $this->mode) {
- return new Auth_OpenID_AX_Error(
- sprintf(
- "Expected mode '%s'; got '%s'",
- $this->mode, $mode));
- }
-
- return true;
- }
-
- /**
- * Return a set of attribute exchange arguments containing the
- * basic information that must be in every attribute exchange
- * message.
- *
- * @access private
- */
- function _newArgs()
- {
- return array('mode' => $this->mode);
- }
-}
-
-/**
- * Represents a single attribute in an attribute exchange
- * request. This should be added to an AXRequest object in order to
- * request the attribute.
- *
- * @package OpenID
- */
-class Auth_OpenID_AX_AttrInfo {
- /**
- * Construct an attribute information object. Do not call this
- * directly; call make(...) instead.
- *
- * @param string $type_uri The type URI for this attribute.
- *
- * @param int $count The number of values of this type to request.
- *
- * @param bool $required Whether the attribute will be marked as
- * required in the request.
- *
- * @param string $alias The name that should be given to this
- * attribute in the request.
- */
- function Auth_OpenID_AX_AttrInfo($type_uri, $count, $required,
- $alias)
- {
- /**
- * required: Whether the attribute will be marked as required
- * when presented to the subject of the attribute exchange
- * request.
- */
- $this->required = $required;
-
- /**
- * count: How many values of this type to request from the
- * subject. Defaults to one.
- */
- $this->count = $count;
-
- /**
- * type_uri: The identifier that determines what the attribute
- * represents and how it is serialized. For example, one type
- * URI representing dates could represent a Unix timestamp in
- * base 10 and another could represent a human-readable
- * string.
- */
- $this->type_uri = $type_uri;
-
- /**
- * alias: The name that should be given to this attribute in
- * the request. If it is not supplied, a generic name will be
- * assigned. For example, if you want to call a Unix timestamp
- * value 'tstamp', set its alias to that value. If two
- * attributes in the same message request to use the same
- * alias, the request will fail to be generated.
- */
- $this->alias = $alias;
- }
-
- /**
- * Construct an attribute information object. For parameter
- * details, see the constructor.
- */
- static function make($type_uri, $count=1, $required=false,
- $alias=null)
- {
- if ($alias !== null) {
- $result = Auth_OpenID_AX_checkAlias($alias);
-
- if (Auth_OpenID_AX::isError($result)) {
- return $result;
- }
- }
-
- return new Auth_OpenID_AX_AttrInfo($type_uri, $count, $required,
- $alias);
- }
-
- /**
- * When processing a request for this attribute, the OP should
- * call this method to determine whether all available attribute
- * values were requested. If self.count == UNLIMITED_VALUES, this
- * returns True. Otherwise this returns False, in which case
- * self.count is an integer.
- */
- function wantsUnlimitedValues()
- {
- return $this->count === Auth_OpenID_AX_UNLIMITED_VALUES;
- }
-}
-
-/**
- * Given a namespace mapping and a string containing a comma-separated
- * list of namespace aliases, return a list of type URIs that
- * correspond to those aliases.
- *
- * @param $namespace_map The mapping from namespace URI to alias
- * @param $alias_list_s The string containing the comma-separated
- * list of aliases. May also be None for convenience.
- *
- * @return $seq The list of namespace URIs that corresponds to the
- * supplied list of aliases. If the string was zero-length or None, an
- * empty list will be returned.
- *
- * return null If an alias is present in the list of aliases but
- * is not present in the namespace map.
- */
-function Auth_OpenID_AX_toTypeURIs($namespace_map, $alias_list_s)
-{
- $uris = array();
-
- if ($alias_list_s) {
- foreach (explode(',', $alias_list_s) as $alias) {
- $type_uri = $namespace_map->getNamespaceURI($alias);
- if ($type_uri === null) {
- // raise KeyError(
- // 'No type is defined for attribute name %r' % (alias,))
- return new Auth_OpenID_AX_Error(
- sprintf('No type is defined for attribute name %s',
- $alias)
- );
- } else {
- $uris[] = $type_uri;
- }
- }
- }
-
- return $uris;
-}
-
-/**
- * An attribute exchange 'fetch_request' message. This message is sent
- * by a relying party when it wishes to obtain attributes about the
- * subject of an OpenID authentication request.
- *
- * @package OpenID
- */
-class Auth_OpenID_AX_FetchRequest extends Auth_OpenID_AX_Message {
-
- var $mode = 'fetch_request';
-
- function Auth_OpenID_AX_FetchRequest($update_url=null)
- {
- /**
- * requested_attributes: The attributes that have been
- * requested thus far, indexed by the type URI.
- */
- $this->requested_attributes = array();
-
- /**
- * update_url: A URL that will accept responses for this
- * attribute exchange request, even in the absence of the user
- * who made this request.
- */
- $this->update_url = $update_url;
- }
-
- /**
- * Add an attribute to this attribute exchange request.
- *
- * @param attribute: The attribute that is being requested
- * @return true on success, false when the requested attribute is
- * already present in this fetch request.
- */
- function add($attribute)
- {
- if ($this->contains($attribute->type_uri)) {
- return new Auth_OpenID_AX_Error(
- sprintf("The attribute %s has already been requested",
- $attribute->type_uri));
- }
-
- $this->requested_attributes[$attribute->type_uri] = $attribute;
-
- return true;
- }
-
- /**
- * Get the serialized form of this attribute fetch request.
- *
- * @returns Auth_OpenID_AX_FetchRequest The fetch request message parameters
- */
- function getExtensionArgs()
- {
- $aliases = new Auth_OpenID_NamespaceMap();
-
- $required = array();
- $if_available = array();
-
- $ax_args = $this->_newArgs();
-
- foreach ($this->requested_attributes as $type_uri => $attribute) {
- if ($attribute->alias === null) {
- $alias = $aliases->add($type_uri);
- } else {
- $alias = $aliases->addAlias($type_uri, $attribute->alias);
-
- if ($alias === null) {
- return new Auth_OpenID_AX_Error(
- sprintf("Could not add alias %s for URI %s",
- $attribute->alias, $type_uri
- ));
- }
- }
-
- if ($attribute->required) {
- $required[] = $alias;
- } else {
- $if_available[] = $alias;
- }
-
- if ($attribute->count != 1) {
- $ax_args['count.' . $alias] = strval($attribute->count);
- }
-
- $ax_args['type.' . $alias] = $type_uri;
- }
-
- if ($required) {
- $ax_args['required'] = implode(',', $required);
- }
-
- if ($if_available) {
- $ax_args['if_available'] = implode(',', $if_available);
- }
-
- return $ax_args;
- }
-
- /**
- * Get the type URIs for all attributes that have been marked as
- * required.
- *
- * @return A list of the type URIs for attributes that have been
- * marked as required.
- */
- function getRequiredAttrs()
- {
- $required = array();
- foreach ($this->requested_attributes as $type_uri => $attribute) {
- if ($attribute->required) {
- $required[] = $type_uri;
- }
- }
-
- return $required;
- }
-
- /**
- * Extract a FetchRequest from an OpenID message
- *
- * @param request: The OpenID request containing the attribute
- * fetch request
- *
- * @returns mixed An Auth_OpenID_AX_Error or the
- * Auth_OpenID_AX_FetchRequest extracted from the request message if
- * successful
- */
- static function fromOpenIDRequest($request)
- {
- $m = $request->message;
- $obj = new Auth_OpenID_AX_FetchRequest();
- $ax_args = $m->getArgs($obj->ns_uri);
-
- $result = $obj->parseExtensionArgs($ax_args);
-
- if (Auth_OpenID_AX::isError($result)) {
- return $result;
- }
-
- if ($obj->update_url) {
- // Update URL must match the openid.realm of the
- // underlying OpenID 2 message.
- $realm = $m->getArg(Auth_OpenID_OPENID_NS, 'realm',
- $m->getArg(
- Auth_OpenID_OPENID_NS,
- 'return_to'));
-
- if (!$realm) {
- $obj = new Auth_OpenID_AX_Error(
- sprintf("Cannot validate update_url %s " .
- "against absent realm", $obj->update_url));
- } else if (!Auth_OpenID_TrustRoot::match($realm,
- $obj->update_url)) {
- $obj = new Auth_OpenID_AX_Error(
- sprintf("Update URL %s failed validation against realm %s",
- $obj->update_url, $realm));
- }
- }
-
- return $obj;
- }
-
- /**
- * Given attribute exchange arguments, populate this FetchRequest.
- *
- * @return $result Auth_OpenID_AX_Error if the data to be parsed
- * does not follow the attribute exchange specification. At least
- * when 'if_available' or 'required' is not specified for a
- * particular attribute type. Returns true otherwise.
- */
- function parseExtensionArgs($ax_args)
- {
- $result = $this->_checkMode($ax_args);
- if (Auth_OpenID_AX::isError($result)) {
- return $result;
- }
-
- $aliases = new Auth_OpenID_NamespaceMap();
-
- foreach ($ax_args as $key => $value) {
- if (strpos($key, 'type.') === 0) {
- $alias = substr($key, 5);
- $type_uri = $value;
-
- $alias = $aliases->addAlias($type_uri, $alias);
-
- if ($alias === null) {
- return new Auth_OpenID_AX_Error(
- sprintf("Could not add alias %s for URI %s",
- $alias, $type_uri)
- );
- }
-
- $count_s = Auth_OpenID::arrayGet($ax_args, 'count.' . $alias);
- if ($count_s) {
- $count = Auth_OpenID::intval($count_s);
- if (($count === false) &&
- ($count_s === Auth_OpenID_AX_UNLIMITED_VALUES)) {
- $count = $count_s;
- }
- } else {
- $count = 1;
- }
-
- if ($count === false) {
- return new Auth_OpenID_AX_Error(
- sprintf("Integer value expected for %s, got %s",
- 'count.' . $alias, $count_s));
- }
-
- $attrinfo = Auth_OpenID_AX_AttrInfo::make($type_uri, $count,
- false, $alias);
-
- if (Auth_OpenID_AX::isError($attrinfo)) {
- return $attrinfo;
- }
-
- $this->add($attrinfo);
- }
- }
-
- $required = Auth_OpenID_AX_toTypeURIs($aliases,
- Auth_OpenID::arrayGet($ax_args, 'required'));
-
- foreach ($required as $type_uri) {
- $attrib = $this->requested_attributes[$type_uri];
- $attrib->required = true;
- }
-
- $if_available = Auth_OpenID_AX_toTypeURIs($aliases,
- Auth_OpenID::arrayGet($ax_args, 'if_available'));
-
- $all_type_uris = array_merge($required, $if_available);
-
- foreach ($aliases->iterNamespaceURIs() as $type_uri) {
- if (!in_array($type_uri, $all_type_uris)) {
- return new Auth_OpenID_AX_Error(
- sprintf('Type URI %s was in the request but not ' .
- 'present in "required" or "if_available"',
- $type_uri));
-
- }
- }
-
- $this->update_url = Auth_OpenID::arrayGet($ax_args, 'update_url');
-
- return true;
- }
-
- /**
- * Iterate over the AttrInfo objects that are contained in this
- * fetch_request.
- */
- function iterAttrs()
- {
- return array_values($this->requested_attributes);
- }
-
- function iterTypes()
- {
- return array_keys($this->requested_attributes);
- }
-
- /**
- * Is the given type URI present in this fetch_request?
- */
- function contains($type_uri)
- {
- return in_array($type_uri, $this->iterTypes());
- }
-}
-
-/**
- * An abstract class that implements a message that has attribute keys
- * and values. It contains the common code between fetch_response and
- * store_request.
- *
- * @package OpenID
- */
-class Auth_OpenID_AX_KeyValueMessage extends Auth_OpenID_AX_Message {
-
- function Auth_OpenID_AX_KeyValueMessage()
- {
- $this->data = array();
- }
-
- /**
- * Add a single value for the given attribute type to the
- * message. If there are already values specified for this type,
- * this value will be sent in addition to the values already
- * specified.
- *
- * @param type_uri: The URI for the attribute
- * @param value: The value to add to the response to the relying
- * party for this attribute
- * @return null
- */
- function addValue($type_uri, $value)
- {
- if (!array_key_exists($type_uri, $this->data)) {
- $this->data[$type_uri] = array();
- }
-
- $values =& $this->data[$type_uri];
- $values[] = $value;
- }
-
- /**
- * Set the values for the given attribute type. This replaces any
- * values that have already been set for this attribute.
- *
- * @param type_uri: The URI for the attribute
- * @param values: A list of values to send for this attribute.
- */
- function setValues($type_uri, &$values)
- {
- $this->data[$type_uri] =& $values;
- }
-
- /**
- * Get the extension arguments for the key/value pairs contained
- * in this message.
- *
- * @param aliases: An alias mapping. Set to None if you don't care
- * about the aliases for this request.
- *
- * @access private
- */
- function _getExtensionKVArgs($aliases)
- {
- if ($aliases === null) {
- $aliases = new Auth_OpenID_NamespaceMap();
- }
-
- $ax_args = array();
-
- foreach ($this->data as $type_uri => $values) {
- $alias = $aliases->add($type_uri);
-
- $ax_args['type.' . $alias] = $type_uri;
- $ax_args['count.' . $alias] = strval(count($values));
-
- foreach ($values as $i => $value) {
- $key = sprintf('value.%s.%d', $alias, $i + 1);
- $ax_args[$key] = $value;
- }
- }
-
- return $ax_args;
- }
-
- /**
- * Parse attribute exchange key/value arguments into this object.
- *
- * @param ax_args: The attribute exchange fetch_response
- * arguments, with namespacing removed.
- *
- * @return Auth_OpenID_AX_Error or true
- */
- function parseExtensionArgs($ax_args)
- {
- $result = $this->_checkMode($ax_args);
- if (Auth_OpenID_AX::isError($result)) {
- return $result;
- }
-
- $aliases = new Auth_OpenID_NamespaceMap();
-
- foreach ($ax_args as $key => $value) {
- if (strpos($key, 'type.') === 0) {
- $type_uri = $value;
- $alias = substr($key, 5);
-
- $result = Auth_OpenID_AX_checkAlias($alias);
-
- if (Auth_OpenID_AX::isError($result)) {
- return $result;
- }
-
- $alias = $aliases->addAlias($type_uri, $alias);
-
- if ($alias === null) {
- return new Auth_OpenID_AX_Error(
- sprintf("Could not add alias %s for URI %s",
- $alias, $type_uri)
- );
- }
- }
- }
-
- foreach ($aliases->iteritems() as $pair) {
- list($type_uri, $alias) = $pair;
-
- if (array_key_exists('count.' . $alias, $ax_args) && ($ax_args['count.' . $alias] !== Auth_OpenID_AX_UNLIMITED_VALUES)) {
-
- $count_key = 'count.' . $alias;
- $count_s = $ax_args[$count_key];
-
- $count = Auth_OpenID::intval($count_s);
-
- if ($count === false) {
- return new Auth_OpenID_AX_Error(
- sprintf("Integer value expected for %s, got %s",
- 'count. %s' . $alias, $count_s,
- Auth_OpenID_AX_UNLIMITED_VALUES)
- );
- }
-
- $values = array();
- for ($i = 1; $i < $count + 1; $i++) {
- $value_key = sprintf('value.%s.%d', $alias, $i);
-
- if (!array_key_exists($value_key, $ax_args)) {
- return new Auth_OpenID_AX_Error(
- sprintf(
- "No value found for key %s",
- $value_key));
- }
-
- $value = $ax_args[$value_key];
- $values[] = $value;
- }
- } else {
- $key = 'value.' . $alias;
-
- if (!array_key_exists($key, $ax_args)) {
- return new Auth_OpenID_AX_Error(
- sprintf(
- "No value found for key %s",
- $key));
- }
-
- $value = $ax_args['value.' . $alias];
-
- if ($value == '') {
- $values = array();
- } else {
- $values = array($value);
- }
- }
-
- $this->data[$type_uri] = $values;
- }
-
- return true;
- }
-
- /**
- * Get a single value for an attribute. If no value was sent for
- * this attribute, use the supplied default. If there is more than
- * one value for this attribute, this method will fail.
- *
- * @param type_uri: The URI for the attribute
- * @param default: The value to return if the attribute was not
- * sent in the fetch_response.
- *
- * @return $value Auth_OpenID_AX_Error on failure or the value of
- * the attribute in the fetch_response message, or the default
- * supplied
- */
- function getSingle($type_uri, $default=null)
- {
- $values = Auth_OpenID::arrayGet($this->data, $type_uri);
- if (!$values) {
- return $default;
- } else if (count($values) == 1) {
- return $values[0];
- } else {
- return new Auth_OpenID_AX_Error(
- sprintf('More than one value present for %s',
- $type_uri)
- );
- }
- }
-
- /**
- * Get the list of values for this attribute in the
- * fetch_response.
- *
- * XXX: what to do if the values are not present? default
- * parameter? this is funny because it's always supposed to return
- * a list, so the default may break that, though it's provided by
- * the user's code, so it might be okay. If no default is
- * supplied, should the return be None or []?
- *
- * @param type_uri: The URI of the attribute
- *
- * @return $values The list of values for this attribute in the
- * response. May be an empty list. If the attribute was not sent
- * in the response, returns Auth_OpenID_AX_Error.
- */
- function get($type_uri)
- {
- if (array_key_exists($type_uri, $this->data)) {
- return $this->data[$type_uri];
- } else {
- return new Auth_OpenID_AX_Error(
- sprintf("Type URI %s not found in response",
- $type_uri)
- );
- }
- }
-
- /**
- * Get the number of responses for a particular attribute in this
- * fetch_response message.
- *
- * @param type_uri: The URI of the attribute
- *
- * @returns int The number of values sent for this attribute. If
- * the attribute was not sent in the response, returns
- * Auth_OpenID_AX_Error.
- */
- function count($type_uri)
- {
- if (array_key_exists($type_uri, $this->data)) {
- return count($this->get($type_uri));
- } else {
- return new Auth_OpenID_AX_Error(
- sprintf("Type URI %s not found in response",
- $type_uri)
- );
- }
- }
-}
-
-/**
- * A fetch_response attribute exchange message.
- *
- * @package OpenID
- */
-class Auth_OpenID_AX_FetchResponse extends Auth_OpenID_AX_KeyValueMessage {
- var $mode = 'fetch_response';
-
- function Auth_OpenID_AX_FetchResponse($update_url=null)
- {
- $this->Auth_OpenID_AX_KeyValueMessage();
- $this->update_url = $update_url;
- }
-
- /**
- * Serialize this object into arguments in the attribute exchange
- * namespace
- *
- * @return $args The dictionary of unqualified attribute exchange
- * arguments that represent this fetch_response, or
- * Auth_OpenID_AX_Error on error.
- */
- function getExtensionArgs($request=null)
- {
- $aliases = new Auth_OpenID_NamespaceMap();
-
- $zero_value_types = array();
-
- if ($request !== null) {
- // Validate the data in the context of the request (the
- // same attributes should be present in each, and the
- // counts in the response must be no more than the counts
- // in the request)
-
- foreach ($this->data as $type_uri => $unused) {
- if (!$request->contains($type_uri)) {
- return new Auth_OpenID_AX_Error(
- sprintf("Response attribute not present in request: %s",
- $type_uri)
- );
- }
- }
-
- foreach ($request->iterAttrs() as $attr_info) {
- // Copy the aliases from the request so that reading
- // the response in light of the request is easier
- if ($attr_info->alias === null) {
- $aliases->add($attr_info->type_uri);
- } else {
- $alias = $aliases->addAlias($attr_info->type_uri,
- $attr_info->alias);
-
- if ($alias === null) {
- return new Auth_OpenID_AX_Error(
- sprintf("Could not add alias %s for URI %s",
- $attr_info->alias, $attr_info->type_uri)
- );
- }
- }
-
- if (array_key_exists($attr_info->type_uri, $this->data)) {
- $values = $this->data[$attr_info->type_uri];
- } else {
- $values = array();
- $zero_value_types[] = $attr_info;
- }
-
- if (($attr_info->count != Auth_OpenID_AX_UNLIMITED_VALUES) &&
- ($attr_info->count < count($values))) {
- return new Auth_OpenID_AX_Error(
- sprintf("More than the number of requested values " .
- "were specified for %s",
- $attr_info->type_uri)
- );
- }
- }
- }
-
- $kv_args = $this->_getExtensionKVArgs($aliases);
-
- // Add the KV args into the response with the args that are
- // unique to the fetch_response
- $ax_args = $this->_newArgs();
-
- // For each requested attribute, put its type/alias and count
- // into the response even if no data were returned.
- foreach ($zero_value_types as $attr_info) {
- $alias = $aliases->getAlias($attr_info->type_uri);
- $kv_args['type.' . $alias] = $attr_info->type_uri;
- $kv_args['count.' . $alias] = '0';
- }
-
- $update_url = null;
- if ($request) {
- $update_url = $request->update_url;
- } else {
- $update_url = $this->update_url;
- }
-
- if ($update_url) {
- $ax_args['update_url'] = $update_url;
- }
-
- Auth_OpenID::update($ax_args, $kv_args);
-
- return $ax_args;
- }
-
- /**
- * @return $result Auth_OpenID_AX_Error on failure or true on
- * success.
- */
- function parseExtensionArgs($ax_args)
- {
- $result = parent::parseExtensionArgs($ax_args);
-
- if (Auth_OpenID_AX::isError($result)) {
- return $result;
- }
-
- $this->update_url = Auth_OpenID::arrayGet($ax_args, 'update_url');
-
- return true;
- }
-
- /**
- * Construct a FetchResponse object from an OpenID library
- * SuccessResponse object.
- *
- * @param success_response: A successful id_res response object
- *
- * @param signed: Whether non-signed args should be processsed. If
- * True (the default), only signed arguments will be processsed.
- *
- * @return $response A FetchResponse containing the data from the
- * OpenID message
- */
- static function fromSuccessResponse($success_response, $signed=true)
- {
- $obj = new Auth_OpenID_AX_FetchResponse();
- if ($signed) {
- $ax_args = $success_response->getSignedNS($obj->ns_uri);
- } else {
- $ax_args = $success_response->message->getArgs($obj->ns_uri);
- }
- if ($ax_args === null || Auth_OpenID::isFailure($ax_args) ||
- sizeof($ax_args) == 0) {
- return null;
- }
-
- $result = $obj->parseExtensionArgs($ax_args);
- if (Auth_OpenID_AX::isError($result)) {
- #XXX log me
- return null;
- }
- return $obj;
- }
-}
-
-/**
- * A store request attribute exchange message representation.
- *
- * @package OpenID
- */
-class Auth_OpenID_AX_StoreRequest extends Auth_OpenID_AX_KeyValueMessage {
- var $mode = 'store_request';
-
- /**
- * @param array $aliases The namespace aliases to use when making
- * this store response. Leave as None to use defaults.
- */
- function getExtensionArgs($aliases=null)
- {
- $ax_args = $this->_newArgs();
- $kv_args = $this->_getExtensionKVArgs($aliases);
- Auth_OpenID::update($ax_args, $kv_args);
- return $ax_args;
- }
-}
-
-/**
- * An indication that the store request was processed along with this
- * OpenID transaction. Use make(), NOT the constructor, to create
- * response objects.
- *
- * @package OpenID
- */
-class Auth_OpenID_AX_StoreResponse extends Auth_OpenID_AX_Message {
- var $SUCCESS_MODE = 'store_response_success';
- var $FAILURE_MODE = 'store_response_failure';
-
- /**
- * Returns Auth_OpenID_AX_Error on error or an
- * Auth_OpenID_AX_StoreResponse object on success.
- */
- function make($succeeded=true, $error_message=null)
- {
- if (($succeeded) && ($error_message !== null)) {
- return new Auth_OpenID_AX_Error('An error message may only be '.
- 'included in a failing fetch response');
- }
-
- return new Auth_OpenID_AX_StoreResponse($succeeded, $error_message);
- }
-
- function Auth_OpenID_AX_StoreResponse($succeeded=true, $error_message=null)
- {
- if ($succeeded) {
- $this->mode = $this->SUCCESS_MODE;
- } else {
- $this->mode = $this->FAILURE_MODE;
- }
-
- $this->error_message = $error_message;
- }
-
- /**
- * Was this response a success response?
- */
- function succeeded()
- {
- return $this->mode == $this->SUCCESS_MODE;
- }
-
- function getExtensionArgs()
- {
- $ax_args = $this->_newArgs();
- if ((!$this->succeeded()) && $this->error_message) {
- $ax_args['error'] = $this->error_message;
- }
-
- return $ax_args;
- }
-}
-
-
--- a/lib/openid-php/Auth/OpenID/Association.php
+++ /dev/null
@@ -1,611 +1,1 @@
-<?php
-/**
- * This module contains code for dealing with associations between
- * consumers and servers.
- *
- * PHP versions 4 and 5
- *
- * LICENSE: See the COPYING file included in this distribution.
- *
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-
-/**
- * @access private
- */
-require_once 'Auth/OpenID/CryptUtil.php';
-
-/**
- * @access private
- */
-require_once 'Auth/OpenID/KVForm.php';
-
-/**
- * @access private
- */
-require_once 'Auth/OpenID/HMAC.php';
-
-/**
- * This class represents an association between a server and a
- * consumer. In general, users of this library will never see
- * instances of this object. The only exception is if you implement a
- * custom {@link Auth_OpenID_OpenIDStore}.
- *
- * If you do implement such a store, it will need to store the values
- * of the handle, secret, issued, lifetime, and assoc_type instance
- * variables.
- *
- * @package OpenID
- */
-class Auth_OpenID_Association {
-
- /**
- * This is a HMAC-SHA1 specific value.
- *
- * @access private
- */
- var $SIG_LENGTH = 20;
-
- /**
- * The ordering and name of keys as stored by serialize.
- *
- * @access private
- */
- var $assoc_keys = array(
- 'version',
- 'handle',
- 'secret',
- 'issued',
- 'lifetime',
- 'assoc_type'
- );
-
- var $_macs = array(
- 'HMAC-SHA1' => 'Auth_OpenID_HMACSHA1',
- 'HMAC-SHA256' => 'Auth_OpenID_HMACSHA256'
- );
-
- /**
- * This is an alternate constructor (factory method) used by the
- * OpenID consumer library to create associations. OpenID store
- * implementations shouldn't use this constructor.
- *
- * @access private
- *
- * @param integer $expires_in This is the amount of time this
- * association is good for, measured in seconds since the
- * association was issued.
- *
- * @param string $handle This is the handle the server gave this
- * association.
- *
- * @param string secret This is the shared secret the server
- * generated for this association.
- *
- * @param assoc_type This is the type of association this
- * instance represents. The only valid values of this field at
- * this time is 'HMAC-SHA1' and 'HMAC-SHA256', but new types may
- * be defined in the future.
- *
- * @return association An {@link Auth_OpenID_Association}
- * instance.
- */
- static function fromExpiresIn($expires_in, $handle, $secret, $assoc_type)
- {
- $issued = time();
- $lifetime = $expires_in;
- return new Auth_OpenID_Association($handle, $secret,
- $issued, $lifetime, $assoc_type);
- }
-
- /**
- * This is the standard constructor for creating an association.
- * The library should create all of the necessary associations, so
- * this constructor is not part of the external API.
- *
- * @access private
- *
- * @param string $handle This is the handle the server gave this
- * association.
- *
- * @param string $secret This is the shared secret the server
- * generated for this association.
- *
- * @param integer $issued This is the time this association was
- * issued, in seconds since 00:00 GMT, January 1, 1970. (ie, a
- * unix timestamp)
- *
- * @param integer $lifetime This is the amount of time this
- * association is good for, measured in seconds since the
- * association was issued.
- *
- * @param string $assoc_type This is the type of association this
- * instance represents. The only valid values of this field at
- * this time is 'HMAC-SHA1' and 'HMAC-SHA256', but new types may
- * be defined in the future.
- */
- function Auth_OpenID_Association(
- $handle, $secret, $issued, $lifetime, $assoc_type)
- {
- if (!in_array($assoc_type,
- Auth_OpenID_getSupportedAssociationTypes(), true)) {
- $fmt = 'Unsupported association type (%s)';
- trigger_error(sprintf($fmt, $assoc_type), E_USER_ERROR);
- }
-
- $this->handle = $handle;
- $this->secret = $secret;
- $this->issued = $issued;
- $this->lifetime = $lifetime;
- $this->assoc_type = $assoc_type;
- }
-
- /**
- * This returns the number of seconds this association is still
- * valid for, or 0 if the association is no longer valid.
- *
- * @return integer $seconds The number of seconds this association
- * is still valid for, or 0 if the association is no longer valid.
- */
- function getExpiresIn($now = null)
- {
- if ($now == null) {
- $now = time();
- }
-
- return max(0, $this->issued + $this->lifetime - $now);
- }
-
- /**
- * This checks to see if two {@link Auth_OpenID_Association}
- * instances represent the same association.
- *
- * @return bool $result true if the two instances represent the
- * same association, false otherwise.
- */
- function equal($other)
- {
- return ((gettype($this) == gettype($other))
- && ($this->handle == $other->handle)
- && ($this->secret == $other->secret)
- && ($this->issued == $other->issued)
- && ($this->lifetime == $other->lifetime)
- && ($this->assoc_type == $other->assoc_type));
- }
-
- /**
- * Convert an association to KV form.
- *
- * @return string $result String in KV form suitable for
- * deserialization by deserialize.
- */
- function serialize()
- {
- $data = array(
- 'version' => '2',
- 'handle' => $this->handle,
- 'secret' => base64_encode($this->secret),
- 'issued' => strval(intval($this->issued)),
- 'lifetime' => strval(intval($this->lifetime)),
- 'assoc_type' => $this->assoc_type
- );
-
- assert(array_keys($data) == $this->assoc_keys);
-
- return Auth_OpenID_KVForm::fromArray($data, $strict = true);
- }
-
- /**
- * Parse an association as stored by serialize(). This is the
- * inverse of serialize.
- *
- * @param string $assoc_s Association as serialized by serialize()
- * @return Auth_OpenID_Association $result instance of this class
- */
- static function deserialize($class_name, $assoc_s)
- {
- $pairs = Auth_OpenID_KVForm::toArray($assoc_s, $strict = true);
- $keys = array();
- $values = array();
- foreach ($pairs as $key => $value) {
- if (is_array($value)) {
- list($key, $value) = $value;
- }
- $keys[] = $key;
- $values[] = $value;
- }
-
- $class_vars = get_class_vars($class_name);
- $class_assoc_keys = $class_vars['assoc_keys'];
-
- sort($keys);
- sort($class_assoc_keys);
-
- if ($keys != $class_assoc_keys) {
- trigger_error('Unexpected key values: ' . var_export($keys, true),
- E_USER_WARNING);
- return null;
- }
-
- $version = $pairs['version'];
- $handle = $pairs['handle'];
- $secret = $pairs['secret'];
- $issued = $pairs['issued'];
- $lifetime = $pairs['lifetime'];
- $assoc_type = $pairs['assoc_type'];
-
- if ($version != '2') {
- trigger_error('Unknown version: ' . $version, E_USER_WARNING);
- return null;
- }
-
- $issued = intval($issued);
- $lifetime = intval($lifetime);
- $secret = base64_decode($secret);
-
- return new $class_name(
- $handle, $secret, $issued, $lifetime, $assoc_type);
- }
-
- /**
- * Generate a signature for a sequence of (key, value) pairs
- *
- * @access private
- * @param array $pairs The pairs to sign, in order. This is an
- * array of two-tuples.
- * @return string $signature The binary signature of this sequence
- * of pairs
- */
- function sign($pairs)
- {
- $kv = Auth_OpenID_KVForm::fromArray($pairs);
-
- /* Invalid association types should be caught at constructor */
- $callback = $this->_macs[$this->assoc_type];
-
- return call_user_func_array($callback, array($this->secret, $kv));
- }
-
- /**
- * Generate a signature for some fields in a dictionary
- *
- * @access private
- * @param array $fields The fields to sign, in order; this is an
- * array of strings.
- * @param array $data Dictionary of values to sign (an array of
- * string => string pairs).
- * @return string $signature The signature, base64 encoded
- */
- function signMessage($message)
- {
- if ($message->hasKey(Auth_OpenID_OPENID_NS, 'sig') ||
- $message->hasKey(Auth_OpenID_OPENID_NS, 'signed')) {
- // Already has a sig
- return null;
- }
-
- $extant_handle = $message->getArg(Auth_OpenID_OPENID_NS,
- 'assoc_handle');
-
- if ($extant_handle && ($extant_handle != $this->handle)) {
- // raise ValueError("Message has a different association handle")
- return null;
- }
-
- $signed_message = $message;
- $signed_message->setArg(Auth_OpenID_OPENID_NS, 'assoc_handle',
- $this->handle);
-
- $message_keys = array_keys($signed_message->toPostArgs());
- $signed_list = array();
- $signed_prefix = 'openid.';
-
- foreach ($message_keys as $k) {
- if (strpos($k, $signed_prefix) === 0) {
- $signed_list[] = substr($k, strlen($signed_prefix));
- }
- }
-
- $signed_list[] = 'signed';
- sort($signed_list);
-
- $signed_message->setArg(Auth_OpenID_OPENID_NS, 'signed',
- implode(',', $signed_list));
- $sig = $this->getMessageSignature($signed_message);
- $signed_message->setArg(Auth_OpenID_OPENID_NS, 'sig', $sig);
- return $signed_message;
- }
-
- /**
- * Given a {@link Auth_OpenID_Message}, return the key/value pairs
- * to be signed according to the signed list in the message. If
- * the message lacks a signed list, return null.
- *
- * @access private
- */
- function _makePairs($message)
- {
- $signed = $message->getArg(Auth_OpenID_OPENID_NS, 'signed');
- if (!$signed || Auth_OpenID::isFailure($signed)) {
- // raise ValueError('Message has no signed list: %s' % (message,))
- return null;
- }
-
- $signed_list = explode(',', $signed);
- $pairs = array();
- $data = $message->toPostArgs();
- foreach ($signed_list as $field) {
- $pairs[] = array($field, Auth_OpenID::arrayGet($data,
- 'openid.' .
- $field, ''));
- }
- return $pairs;
- }
-
- /**
- * Given an {@link Auth_OpenID_Message}, return the signature for
- * the signed list in the message.
- *
- * @access private
- */
- function getMessageSignature($message)
- {
- $pairs = $this->_makePairs($message);
- return base64_encode($this->sign($pairs));
- }
-
- /**
- * Confirm that the signature of these fields matches the
- * signature contained in the data.
- *
- * @access private
- */
- function checkMessageSignature($message)
- {
- $sig = $message->getArg(Auth_OpenID_OPENID_NS,
- 'sig');
-
- if (!$sig || Auth_OpenID::isFailure($sig)) {
- return false;
- }
-
- $calculated_sig = $this->getMessageSignature($message);
- return Auth_OpenID_CryptUtil::constEq($calculated_sig, $sig);
- }
-}
-
-function Auth_OpenID_getSecretSize($assoc_type)
-{
- if ($assoc_type == 'HMAC-SHA1') {
- return 20;
- } else if ($assoc_type == 'HMAC-SHA256') {
- return 32;
- } else {
- return null;
- }
-}
-
-function Auth_OpenID_getAllAssociationTypes()
-{
- return array('HMAC-SHA1', 'HMAC-SHA256');
-}
-
-function Auth_OpenID_getSupportedAssociationTypes()
-{
- $a = array('HMAC-SHA1');
-
- if (Auth_OpenID_HMACSHA256_SUPPORTED) {
- $a[] = 'HMAC-SHA256';
- }
-
- return $a;
-}
-
-function Auth_OpenID_getSessionTypes($assoc_type)
-{
- $assoc_to_session = array(
- 'HMAC-SHA1' => array('DH-SHA1', 'no-encryption'));
-
- if (Auth_OpenID_HMACSHA256_SUPPORTED) {
- $assoc_to_session['HMAC-SHA256'] =
- array('DH-SHA256', 'no-encryption');
- }
-
- return Auth_OpenID::arrayGet($assoc_to_session, $assoc_type, array());
-}
-
-function Auth_OpenID_checkSessionType($assoc_type, $session_type)
-{
- if (!in_array($session_type,
- Auth_OpenID_getSessionTypes($assoc_type))) {
- return false;
- }
-
- return true;
-}
-
-function Auth_OpenID_getDefaultAssociationOrder()
-{
- $order = array();
-
- if (!Auth_OpenID_noMathSupport()) {
- $order[] = array('HMAC-SHA1', 'DH-SHA1');
-
- if (Auth_OpenID_HMACSHA256_SUPPORTED) {
- $order[] = array('HMAC-SHA256', 'DH-SHA256');
- }
- }
-
- $order[] = array('HMAC-SHA1', 'no-encryption');
-
- if (Auth_OpenID_HMACSHA256_SUPPORTED) {
- $order[] = array('HMAC-SHA256', 'no-encryption');
- }
-
- return $order;
-}
-
-function Auth_OpenID_getOnlyEncryptedOrder()
-{
- $result = array();
-
- foreach (Auth_OpenID_getDefaultAssociationOrder() as $pair) {
- list($assoc, $session) = $pair;
-
- if ($session != 'no-encryption') {
- if (Auth_OpenID_HMACSHA256_SUPPORTED &&
- ($assoc == 'HMAC-SHA256')) {
- $result[] = $pair;
- } else if ($assoc != 'HMAC-SHA256') {
- $result[] = $pair;
- }
- }
- }
-
- return $result;
-}
-
-function Auth_OpenID_getDefaultNegotiator()
-{
- return new Auth_OpenID_SessionNegotiator(
- Auth_OpenID_getDefaultAssociationOrder());
-}
-
-function Auth_OpenID_getEncryptedNegotiator()
-{
- return new Auth_OpenID_SessionNegotiator(
- Auth_OpenID_getOnlyEncryptedOrder());
-}
-
-/**
- * A session negotiator controls the allowed and preferred association
- * types and association session types. Both the {@link
- * Auth_OpenID_Consumer} and {@link Auth_OpenID_Server} use
- * negotiators when creating associations.
- *
- * You can create and use negotiators if you:
-
- * - Do not want to do Diffie-Hellman key exchange because you use
- * transport-layer encryption (e.g. SSL)
- *
- * - Want to use only SHA-256 associations
- *
- * - Do not want to support plain-text associations over a non-secure
- * channel
- *
- * It is up to you to set a policy for what kinds of associations to
- * accept. By default, the library will make any kind of association
- * that is allowed in the OpenID 2.0 specification.
- *
- * Use of negotiators in the library
- * =================================
- *
- * When a consumer makes an association request, it calls {@link
- * getAllowedType} to get the preferred association type and
- * association session type.
- *
- * The server gets a request for a particular association/session type
- * and calls {@link isAllowed} to determine if it should create an
- * association. If it is supported, negotiation is complete. If it is
- * not, the server calls {@link getAllowedType} to get an allowed
- * association type to return to the consumer.
- *
- * If the consumer gets an error response indicating that the
- * requested association/session type is not supported by the server
- * that contains an assocation/session type to try, it calls {@link
- * isAllowed} to determine if it should try again with the given
- * combination of association/session type.
- *
- * @package OpenID
- */
-class Auth_OpenID_SessionNegotiator {
- function Auth_OpenID_SessionNegotiator($allowed_types)
- {
- $this->allowed_types = array();
- $this->setAllowedTypes($allowed_types);
- }
-
- /**
- * Set the allowed association types, checking to make sure each
- * combination is valid.
- *
- * @access private
- */
- function setAllowedTypes($allowed_types)
- {
- foreach ($allowed_types as $pair) {
- list($assoc_type, $session_type) = $pair;
- if (!Auth_OpenID_checkSessionType($assoc_type, $session_type)) {
- return false;
- }
- }
-
- $this->allowed_types = $allowed_types;
- return true;
- }
-
- /**
- * Add an association type and session type to the allowed types
- * list. The assocation/session pairs are tried in the order that
- * they are added.
- *
- * @access private
- */
- function addAllowedType($assoc_type, $session_type = null)
- {
- if ($this->allowed_types === null) {
- $this->allowed_types = array();
- }
-
- if ($session_type === null) {
- $available = Auth_OpenID_getSessionTypes($assoc_type);
-
- if (!$available) {
- return false;
- }
-
- foreach ($available as $session_type) {
- $this->addAllowedType($assoc_type, $session_type);
- }
- } else {
- if (Auth_OpenID_checkSessionType($assoc_type, $session_type)) {
- $this->allowed_types[] = array($assoc_type, $session_type);
- } else {
- return false;
- }
- }
-
- return true;
- }
-
- // Is this combination of association type and session type allowed?
- function isAllowed($assoc_type, $session_type)
- {
- $assoc_good = in_array(array($assoc_type, $session_type),
- $this->allowed_types);
-
- $matches = in_array($session_type,
- Auth_OpenID_getSessionTypes($assoc_type));
-
- return ($assoc_good && $matches);
- }
-
- /**
- * Get a pair of assocation type and session type that are
- * supported.
- */
- function getAllowedType()
- {
- if (!$this->allowed_types) {
- return array(null, null);
- }
-
- return $this->allowed_types[0];
- }
-}
-
-
--- a/lib/openid-php/Auth/OpenID/BigMath.php
+++ /dev/null
@@ -1,452 +1,1 @@
-<?php
-/**
- * BigMath: A math library wrapper that abstracts out the underlying
- * long integer library.
- *
- * PHP versions 4 and 5
- *
- * LICENSE: See the COPYING file included in this distribution.
- *
- * @access private
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-
-/**
- * Needed for random number generation
- */
-require_once 'Auth/OpenID/CryptUtil.php';
-
-/**
- * Need Auth_OpenID::bytes().
- */
-require_once 'Auth/OpenID.php';
-
-/**
- * The superclass of all big-integer math implementations
- * @access private
- * @package OpenID
- */
-class Auth_OpenID_MathLibrary {
- /**
- * Given a long integer, returns the number converted to a binary
- * string. This function accepts long integer values of arbitrary
- * magnitude and uses the local large-number math library when
- * available.
- *
- * @param integer $long The long number (can be a normal PHP
- * integer or a number created by one of the available long number
- * libraries)
- * @return string $binary The binary version of $long
- */
- function longToBinary($long)
- {
- $cmp = $this->cmp($long, 0);
- if ($cmp < 0) {
- $msg = __FUNCTION__ . " takes only positive integers.";
- trigger_error($msg, E_USER_ERROR);
- return null;
- }
-
- if ($cmp == 0) {
- return "\x00";
- }
-
- $bytes = array();
-
- while ($this->cmp($long, 0) > 0) {
- array_unshift($bytes, $this->mod($long, 256));
- $long = $this->div($long, pow(2, 8));
- }
-
- if ($bytes && ($bytes[0] > 127)) {
- array_unshift($bytes, 0);
- }
-
- $string = '';
- foreach ($bytes as $byte) {
- $string .= pack('C', $byte);
- }
-
- return $string;
- }
-
- /**
- * Given a binary string, returns the binary string converted to a
- * long number.
- *
- * @param string $binary The binary version of a long number,
- * probably as a result of calling longToBinary
- * @return integer $long The long number equivalent of the binary
- * string $str
- */
- function binaryToLong($str)
- {
- if ($str === null) {
- return null;
- }
-
- // Use array_merge to return a zero-indexed array instead of a
- // one-indexed array.
- $bytes = array_merge(unpack('C*', $str));
-
- $n = $this->init(0);
-
- if ($bytes && ($bytes[0] > 127)) {
- trigger_error("bytesToNum works only for positive integers.",
- E_USER_WARNING);
- return null;
- }
-
- foreach ($bytes as $byte) {
- $n = $this->mul($n, pow(2, 8));
- $n = $this->add($n, $byte);
- }
-
- return $n;
- }
-
- function base64ToLong($str)
- {
- $b64 = base64_decode($str);
-
- if ($b64 === false) {
- return false;
- }
-
- return $this->binaryToLong($b64);
- }
-
- function longToBase64($str)
- {
- return base64_encode($this->longToBinary($str));
- }
-
- /**
- * Returns a random number in the specified range. This function
- * accepts $start, $stop, and $step values of arbitrary magnitude
- * and will utilize the local large-number math library when
- * available.
- *
- * @param integer $start The start of the range, or the minimum
- * random number to return
- * @param integer $stop The end of the range, or the maximum
- * random number to return
- * @param integer $step The step size, such that $result - ($step
- * * N) = $start for some N
- * @return integer $result The resulting randomly-generated number
- */
- function rand($stop)
- {
- static $duplicate_cache = array();
-
- // Used as the key for the duplicate cache
- $rbytes = $this->longToBinary($stop);
-
- if (array_key_exists($rbytes, $duplicate_cache)) {
- list($duplicate, $nbytes) = $duplicate_cache[$rbytes];
- } else {
- if ($rbytes[0] == "\x00") {
- $nbytes = Auth_OpenID::bytes($rbytes) - 1;
- } else {
- $nbytes = Auth_OpenID::bytes($rbytes);
- }
-
- $mxrand = $this->pow(256, $nbytes);
-
- // If we get a number less than this, then it is in the
- // duplicated range.
- $duplicate = $this->mod($mxrand, $stop);
-
- if (count($duplicate_cache) > 10) {
- $duplicate_cache = array();
- }
-
- $duplicate_cache[$rbytes] = array($duplicate, $nbytes);
- }
-
- do {
- $bytes = "\x00" . Auth_OpenID_CryptUtil::getBytes($nbytes);
- $n = $this->binaryToLong($bytes);
- // Keep looping if this value is in the low duplicated range
- } while ($this->cmp($n, $duplicate) < 0);
-
- return $this->mod($n, $stop);
- }
-}
-
-/**
- * Exposes BCmath math library functionality.
- *
- * {@link Auth_OpenID_BcMathWrapper} wraps the functionality provided
- * by the BCMath extension.
- *
- * @access private
- * @package OpenID
- */
-class Auth_OpenID_BcMathWrapper extends Auth_OpenID_MathLibrary{
- var $type = 'bcmath';
-
- function add($x, $y)
- {
- return bcadd($x, $y);
- }
-
- function sub($x, $y)
- {
- return bcsub($x, $y);
- }
-
- function pow($base, $exponent)
- {
- return bcpow($base, $exponent);
- }
-
- function cmp($x, $y)
- {
- return bccomp($x, $y);
- }
-
- function init($number, $base = 10)
- {
- return $number;
- }
-
- function mod($base, $modulus)
- {
- return bcmod($base, $modulus);
- }
-
- function mul($x, $y)
- {
- return bcmul($x, $y);
- }
-
- function div($x, $y)
- {
- return bcdiv($x, $y);
- }
-
- /**
- * Same as bcpowmod when bcpowmod is missing
- *
- * @access private
- */
- function _powmod($base, $exponent, $modulus)
- {
- $square = $this->mod($base, $modulus);
- $result = 1;
- while($this->cmp($exponent, 0) > 0) {
- if ($this->mod($exponent, 2)) {
- $result = $this->mod($this->mul($result, $square), $modulus);
- }
- $square = $this->mod($this->mul($square, $square), $modulus);
- $exponent = $this->div($exponent, 2);
- }
- return $result;
- }
-
- function powmod($base, $exponent, $modulus)
- {
- if (function_exists('bcpowmod')) {
- return bcpowmod($base, $exponent, $modulus);
- } else {
- return $this->_powmod($base, $exponent, $modulus);
- }
- }
-
- function toString($num)
- {
- return $num;
- }
-}
-
-/**
- * Exposes GMP math library functionality.
- *
- * {@link Auth_OpenID_GmpMathWrapper} wraps the functionality provided
- * by the GMP extension.
- *
- * @access private
- * @package OpenID
- */
-class Auth_OpenID_GmpMathWrapper extends Auth_OpenID_MathLibrary{
- var $type = 'gmp';
-
- function add($x, $y)
- {
- return gmp_add($x, $y);
- }
-
- function sub($x, $y)
- {
- return gmp_sub($x, $y);
- }
-
- function pow($base, $exponent)
- {
- return gmp_pow($base, $exponent);
- }
-
- function cmp($x, $y)
- {
- return gmp_cmp($x, $y);
- }
-
- function init($number, $base = 10)
- {
- return gmp_init($number, $base);
- }
-
- function mod($base, $modulus)
- {
- return gmp_mod($base, $modulus);
- }
-
- function mul($x, $y)
- {
- return gmp_mul($x, $y);
- }
-
- function div($x, $y)
- {
- return gmp_div_q($x, $y);
- }
-
- function powmod($base, $exponent, $modulus)
- {
- return gmp_powm($base, $exponent, $modulus);
- }
-
- function toString($num)
- {
- return gmp_strval($num);
- }
-}
-
-/**
- * Define the supported extensions. An extension array has keys
- * 'modules', 'extension', and 'class'. 'modules' is an array of PHP
- * module names which the loading code will attempt to load. These
- * values will be suffixed with a library file extension (e.g. ".so").
- * 'extension' is the name of a PHP extension which will be tested
- * before 'modules' are loaded. 'class' is the string name of a
- * {@link Auth_OpenID_MathWrapper} subclass which should be
- * instantiated if a given extension is present.
- *
- * You can define new math library implementations and add them to
- * this array.
- */
-function Auth_OpenID_math_extensions()
-{
- $result = array();
-
- if (!defined('Auth_OpenID_BUGGY_GMP')) {
- $result[] =
- array('modules' => array('gmp', 'php_gmp'),
- 'extension' => 'gmp',
- 'class' => 'Auth_OpenID_GmpMathWrapper');
- }
-
- $result[] = array('modules' => array('bcmath', 'php_bcmath'),
- 'extension' => 'bcmath',
- 'class' => 'Auth_OpenID_BcMathWrapper');
-
- return $result;
-}
-
-/**
- * Detect which (if any) math library is available
- */
-function Auth_OpenID_detectMathLibrary($exts)
-{
- $loaded = false;
-
- foreach ($exts as $extension) {
- if (extension_loaded($extension['extension'])) {
- return $extension;
- }
- }
-
- return false;
-}
-
-/**
- * {@link Auth_OpenID_getMathLib} checks for the presence of long
- * number extension modules and returns an instance of
- * {@link Auth_OpenID_MathWrapper} which exposes the module's
- * functionality.
- *
- * Checks for the existence of an extension module described by the
- * result of {@link Auth_OpenID_math_extensions()} and returns an
- * instance of a wrapper for that extension module. If no extension
- * module is found, an instance of {@link Auth_OpenID_MathWrapper} is
- * returned, which wraps the native PHP integer implementation. The
- * proper calling convention for this method is $lib =
- * Auth_OpenID_getMathLib().
- *
- * This function checks for the existence of specific long number
- * implementations in the following order: GMP followed by BCmath.
- *
- * @return Auth_OpenID_MathWrapper $instance An instance of
- * {@link Auth_OpenID_MathWrapper} or one of its subclasses
- *
- * @package OpenID
- */
-function Auth_OpenID_getMathLib()
-{
- // The instance of Auth_OpenID_MathWrapper that we choose to
- // supply will be stored here, so that subseqent calls to this
- // method will return a reference to the same object.
- static $lib = null;
-
- if (isset($lib)) {
- return $lib;
- }
-
- if (Auth_OpenID_noMathSupport()) {
- $null = null;
- return $null;
- }
-
- // If this method has not been called before, look at
- // Auth_OpenID_math_extensions and try to find an extension that
- // works.
- $ext = Auth_OpenID_detectMathLibrary(Auth_OpenID_math_extensions());
- if ($ext === false) {
- $tried = array();
- foreach (Auth_OpenID_math_extensions() as $extinfo) {
- $tried[] = $extinfo['extension'];
- }
- $triedstr = implode(", ", $tried);
-
- Auth_OpenID_setNoMathSupport();
-
- $result = null;
- return $result;
- }
-
- // Instantiate a new wrapper
- $class = $ext['class'];
- $lib = new $class();
-
- return $lib;
-}
-
-function Auth_OpenID_setNoMathSupport()
-{
- if (!defined('Auth_OpenID_NO_MATH_SUPPORT')) {
- define('Auth_OpenID_NO_MATH_SUPPORT', true);
- }
-}
-
-function Auth_OpenID_noMathSupport()
-{
- return defined('Auth_OpenID_NO_MATH_SUPPORT');
-}
-
-
-
--- a/lib/openid-php/Auth/OpenID/CryptUtil.php
+++ /dev/null
@@ -1,123 +1,1 @@
-<?php
-/**
- * CryptUtil: A suite of wrapper utility functions for the OpenID
- * library.
- *
- * PHP versions 4 and 5
- *
- * LICENSE: See the COPYING file included in this distribution.
- *
- * @access private
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-
-if (!defined('Auth_OpenID_RAND_SOURCE')) {
- /**
- * The filename for a source of random bytes. Define this yourself
- * if you have a different source of randomness.
- */
- define('Auth_OpenID_RAND_SOURCE', '/dev/urandom');
-}
-
-class Auth_OpenID_CryptUtil {
- /**
- * Get the specified number of random bytes.
- *
- * Attempts to use a cryptographically secure (not predictable)
- * source of randomness if available. If there is no high-entropy
- * randomness source available, it will fail. As a last resort,
- * for non-critical systems, define
- * <code>Auth_OpenID_RAND_SOURCE</code> as <code>null</code>, and
- * the code will fall back on a pseudo-random number generator.
- *
- * @param int $num_bytes The length of the return value
- * @return string $bytes random bytes
- */
- static function getBytes($num_bytes)
- {
- static $f = null;
- $bytes = '';
- if ($f === null) {
- if (Auth_OpenID_RAND_SOURCE === null) {
- $f = false;
- } else {
- $f = @fopen(Auth_OpenID_RAND_SOURCE, "r");
- /*if ($f === false) {
- $msg = 'Define Auth_OpenID_RAND_SOURCE as null to ' .
- ' continue with an insecure random number generator.';
- trigger_error($msg, E_USER_ERROR);
- }*/
- }
- }
- if ($f === false) {
- // pseudorandom used
- $bytes = '';
- for ($i = 0; $i < $num_bytes; $i += 4) {
- $bytes .= pack('L', mt_rand());
- }
- $bytes = substr($bytes, 0, $num_bytes);
- } else {
- $bytes = fread($f, $num_bytes);
- }
- return $bytes;
- }
-
- /**
- * Produce a string of length random bytes, chosen from chrs. If
- * $chrs is null, the resulting string may contain any characters.
- *
- * @param integer $length The length of the resulting
- * randomly-generated string
- * @param string $chrs A string of characters from which to choose
- * to build the new string
- * @return string $result A string of randomly-chosen characters
- * from $chrs
- */
- static function randomString($length, $population = null)
- {
- if ($population === null) {
- return Auth_OpenID_CryptUtil::getBytes($length);
- }
-
- $popsize = strlen($population);
-
- if ($popsize > 256) {
- $msg = 'More than 256 characters supplied to ' . __FUNCTION__;
- trigger_error($msg, E_USER_ERROR);
- }
-
- $duplicate = 256 % $popsize;
-
- $str = "";
- for ($i = 0; $i < $length; $i++) {
- do {
- $n = ord(Auth_OpenID_CryptUtil::getBytes(1));
- } while ($n < $duplicate);
-
- $n %= $popsize;
- $str .= $population[$n];
- }
-
- return $str;
- }
-
- static function constEq($s1, $s2)
- {
- if (strlen($s1) != strlen($s2)) {
- return false;
- }
-
- $result = true;
- $length = strlen($s1);
- for ($i = 0; $i < $length; $i++) {
- $result &= ($s1[$i] == $s2[$i]);
- }
- return $result;
- }
-}
-
-
--- a/lib/openid-php/Auth/OpenID/DatabaseConnection.php
+++ /dev/null
@@ -1,131 +1,1 @@
-<?php
-/**
- * The Auth_OpenID_DatabaseConnection class, which is used to emulate
- * a PEAR database connection.
- *
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-
-/**
- * An empty base class intended to emulate PEAR connection
- * functionality in applications that supply their own database
- * abstraction mechanisms. See {@link Auth_OpenID_SQLStore} for more
- * information. You should subclass this class if you need to create
- * an SQL store that needs to access its database using an
- * application's database abstraction layer instead of a PEAR database
- * connection. Any subclass of Auth_OpenID_DatabaseConnection MUST
- * adhere to the interface specified here.
- *
- * @package OpenID
- */
-class Auth_OpenID_DatabaseConnection {
- /**
- * Sets auto-commit mode on this database connection.
- *
- * @param bool $mode True if auto-commit is to be used; false if
- * not.
- */
- function autoCommit($mode)
- {
- }
-
- /**
- * Run an SQL query with the specified parameters, if any.
- *
- * @param string $sql An SQL string with placeholders. The
- * placeholders are assumed to be specific to the database engine
- * for this connection.
- *
- * @param array $params An array of parameters to insert into the
- * SQL string using this connection's escaping mechanism.
- *
- * @return mixed $result The result of calling this connection's
- * internal query function. The type of result depends on the
- * underlying database engine. This method is usually used when
- * the result of a query is not important, like a DDL query.
- */
- function query($sql, $params = array())
- {
- }
-
- /**
- * Starts a transaction on this connection, if supported.
- */
- function begin()
- {
- }
-
- /**
- * Commits a transaction on this connection, if supported.
- */
- function commit()
- {
- }
-
- /**
- * Performs a rollback on this connection, if supported.
- */
- function rollback()
- {
- }
-
- /**
- * Run an SQL query and return the first column of the first row
- * of the result set, if any.
- *
- * @param string $sql An SQL string with placeholders. The
- * placeholders are assumed to be specific to the database engine
- * for this connection.
- *
- * @param array $params An array of parameters to insert into the
- * SQL string using this connection's escaping mechanism.
- *
- * @return mixed $result The value of the first column of the
- * first row of the result set. False if no such result was
- * found.
- */
- function getOne($sql, $params = array())
- {
- }
-
- /**
- * Run an SQL query and return the first row of the result set, if
- * any.
- *
- * @param string $sql An SQL string with placeholders. The
- * placeholders are assumed to be specific to the database engine
- * for this connection.
- *
- * @param array $params An array of parameters to insert into the
- * SQL string using this connection's escaping mechanism.
- *
- * @return array $result The first row of the result set, if any,
- * keyed on column name. False if no such result was found.
- */
- function getRow($sql, $params = array())
- {
- }
-
- /**
- * Run an SQL query with the specified parameters, if any.
- *
- * @param string $sql An SQL string with placeholders. The
- * placeholders are assumed to be specific to the database engine
- * for this connection.
- *
- * @param array $params An array of parameters to insert into the
- * SQL string using this connection's escaping mechanism.
- *
- * @return array $result An array of arrays representing the
- * result of the query; each array is keyed on column name.
- */
- function getAll($sql, $params = array())
- {
- }
-}
-
-
--- a/lib/openid-php/Auth/OpenID/DiffieHellman.php
+++ /dev/null
@@ -1,114 +1,1 @@
-<?php
-/**
- * The OpenID library's Diffie-Hellman implementation.
- *
- * PHP versions 4 and 5
- *
- * LICENSE: See the COPYING file included in this distribution.
- *
- * @access private
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-
-require_once 'Auth/OpenID.php';
-require_once 'Auth/OpenID/BigMath.php';
-
-function Auth_OpenID_getDefaultMod()
-{
- return '155172898181473697471232257763715539915724801'.
- '966915404479707795314057629378541917580651227423'.
- '698188993727816152646631438561595825688188889951'.
- '272158842675419950341258706556549803580104870537'.
- '681476726513255747040765857479291291572334510643'.
- '245094715007229621094194349783925984760375594985'.
- '848253359305585439638443';
-}
-
-function Auth_OpenID_getDefaultGen()
-{
- return '2';
-}
-
-/**
- * The Diffie-Hellman key exchange class. This class relies on
- * {@link Auth_OpenID_MathLibrary} to perform large number operations.
- *
- * @access private
- * @package OpenID
- */
-class Auth_OpenID_DiffieHellman {
-
- var $mod;
- var $gen;
- var $private;
- var $lib = null;
-
- function Auth_OpenID_DiffieHellman($mod = null, $gen = null,
- $private = null, $lib = null)
- {
- if ($lib === null) {
- $this->lib = Auth_OpenID_getMathLib();
- } else {
- $this->lib = $lib;
- }
-
- if ($mod === null) {
- $this->mod = $this->lib->init(Auth_OpenID_getDefaultMod());
- } else {
- $this->mod = $mod;
- }
-
- if ($gen === null) {
- $this->gen = $this->lib->init(Auth_OpenID_getDefaultGen());
- } else {
- $this->gen = $gen;
- }
-
- if ($private === null) {
- $r = $this->lib->rand($this->mod);
- $this->private = $this->lib->add($r, 1);
- } else {
- $this->private = $private;
- }
-
- $this->public = $this->lib->powmod($this->gen, $this->private,
- $this->mod);
- }
-
- function getSharedSecret($composite)
- {
- return $this->lib->powmod($composite, $this->private, $this->mod);
- }
-
- function getPublicKey()
- {
- return $this->public;
- }
-
- function usingDefaultValues()
- {
- return ($this->mod == Auth_OpenID_getDefaultMod() &&
- $this->gen == Auth_OpenID_getDefaultGen());
- }
-
- function xorSecret($composite, $secret, $hash_func)
- {
- $dh_shared = $this->getSharedSecret($composite);
- $dh_shared_str = $this->lib->longToBinary($dh_shared);
- $hash_dh_shared = $hash_func($dh_shared_str);
-
- $xsecret = "";
- for ($i = 0; $i < Auth_OpenID::bytes($secret); $i++) {
- $xsecret .= chr(ord($secret[$i]) ^ ord($hash_dh_shared[$i]));
- }
-
- return $xsecret;
- }
-}
-
-
-
--- a/lib/openid-php/Auth/OpenID/Discover.php
+++ /dev/null
@@ -1,607 +1,1 @@
-<?php
-/**
- * The OpenID and Yadis discovery implementation for OpenID 1.2.
- */
-
-require_once "Auth/OpenID.php";
-require_once "Auth/OpenID/Parse.php";
-require_once "Auth/OpenID/Message.php";
-require_once "Auth/Yadis/XRIRes.php";
-require_once "Auth/Yadis/Yadis.php";
-
-// XML namespace value
-define('Auth_OpenID_XMLNS_1_0', 'http://openid.net/xmlns/1.0');
-
-// Yadis service types
-define('Auth_OpenID_TYPE_1_2', 'http://openid.net/signon/1.2');
-define('Auth_OpenID_TYPE_1_1', 'http://openid.net/signon/1.1');
-define('Auth_OpenID_TYPE_1_0', 'http://openid.net/signon/1.0');
-define('Auth_OpenID_TYPE_2_0_IDP', 'http://specs.openid.net/auth/2.0/server');
-define('Auth_OpenID_TYPE_2_0', 'http://specs.openid.net/auth/2.0/signon');
-define('Auth_OpenID_RP_RETURN_TO_URL_TYPE',
- 'http://specs.openid.net/auth/2.0/return_to');
-
-function Auth_OpenID_getOpenIDTypeURIs()
-{
- return array(Auth_OpenID_TYPE_2_0_IDP,
- Auth_OpenID_TYPE_2_0,
- Auth_OpenID_TYPE_1_2,
- Auth_OpenID_TYPE_1_1,
- Auth_OpenID_TYPE_1_0);
-}
-
-function Auth_OpenID_getOpenIDConsumerTypeURIs()
-{
- return array(Auth_OpenID_RP_RETURN_TO_URL_TYPE);
-}
-
-
-/*
- * Provides a user-readable interpretation of a type uri.
- * Useful for error messages.
- */
-function Auth_OpenID_getOpenIDTypeName($type_uri) {
- switch ($type_uri) {
- case Auth_OpenID_TYPE_2_0_IDP:
- return 'OpenID 2.0 IDP';
- case Auth_OpenID_TYPE_2_0:
- return 'OpenID 2.0';
- case Auth_OpenID_TYPE_1_2:
- return 'OpenID 1.2';
- case Auth_OpenID_TYPE_1_1:
- return 'OpenID 1.1';
- case Auth_OpenID_TYPE_1_0:
- return 'OpenID 1.0';
- case Auth_OpenID_RP_RETURN_TO_URL_TYPE:
- return 'OpenID relying party';
- }
-}
-
-/**
- * Object representing an OpenID service endpoint.
- */
-class Auth_OpenID_ServiceEndpoint {
- function Auth_OpenID_ServiceEndpoint()
- {
- $this->claimed_id = null;
- $this->server_url = null;
- $this->type_uris = array();
- $this->local_id = null;
- $this->canonicalID = null;
- $this->used_yadis = false; // whether this came from an XRDS
- $this->display_identifier = null;
- }
-
- function getDisplayIdentifier()
- {
- if ($this->display_identifier) {
- return $this->display_identifier;
- }
- if (! $this->claimed_id) {
- return $this->claimed_id;
- }
- $parsed = parse_url($this->claimed_id);
- $scheme = $parsed['scheme'];
- $host = $parsed['host'];
- $path = $parsed['path'];
- if (array_key_exists('query', $parsed)) {
- $query = $parsed['query'];
- $no_frag = "$scheme://$host$path?$query";
- } else {
- $no_frag = "$scheme://$host$path";
- }
- return $no_frag;
- }
-
- function usesExtension($extension_uri)
- {
- return in_array($extension_uri, $this->type_uris);
- }
-
- function preferredNamespace()
- {
- if (in_array(Auth_OpenID_TYPE_2_0_IDP, $this->type_uris) ||
- in_array(Auth_OpenID_TYPE_2_0, $this->type_uris)) {
- return Auth_OpenID_OPENID2_NS;
- } else {
- return Auth_OpenID_OPENID1_NS;
- }
- }
-
- /*
- * Query this endpoint to see if it has any of the given type
- * URIs. This is useful for implementing other endpoint classes
- * that e.g. need to check for the presence of multiple versions
- * of a single protocol.
- *
- * @param $type_uris The URIs that you wish to check
- *
- * @return all types that are in both in type_uris and
- * $this->type_uris
- */
- function matchTypes($type_uris)
- {
- $result = array();
- foreach ($type_uris as $test_uri) {
- if ($this->supportsType($test_uri)) {
- $result[] = $test_uri;
- }
- }
-
- return $result;
- }
-
- function supportsType($type_uri)
- {
- // Does this endpoint support this type?
- return ((in_array($type_uri, $this->type_uris)) ||
- (($type_uri == Auth_OpenID_TYPE_2_0) &&
- $this->isOPIdentifier()));
- }
-
- function compatibilityMode()
- {
- return $this->preferredNamespace() != Auth_OpenID_OPENID2_NS;
- }
-
- function isOPIdentifier()
- {
- return in_array(Auth_OpenID_TYPE_2_0_IDP, $this->type_uris);
- }
-
- static function fromOPEndpointURL($op_endpoint_url)
- {
- // Construct an OP-Identifier OpenIDServiceEndpoint object for
- // a given OP Endpoint URL
- $obj = new Auth_OpenID_ServiceEndpoint();
- $obj->server_url = $op_endpoint_url;
- $obj->type_uris = array(Auth_OpenID_TYPE_2_0_IDP);
- return $obj;
- }
-
- function parseService($yadis_url, $uri, $type_uris, $service_element)
- {
- // Set the state of this object based on the contents of the
- // service element. Return true if successful, false if not
- // (if findOPLocalIdentifier returns false).
- $this->type_uris = $type_uris;
- $this->server_url = $uri;
- $this->used_yadis = true;
-
- if (!$this->isOPIdentifier()) {
- $this->claimed_id = $yadis_url;
- $this->local_id = Auth_OpenID_findOPLocalIdentifier(
- $service_element,
- $this->type_uris);
- if ($this->local_id === false) {
- return false;
- }
- }
-
- return true;
- }
-
- function getLocalID()
- {
- // Return the identifier that should be sent as the
- // openid.identity_url parameter to the server.
- if ($this->local_id === null && $this->canonicalID === null) {
- return $this->claimed_id;
- } else {
- if ($this->local_id) {
- return $this->local_id;
- } else {
- return $this->canonicalID;
- }
- }
- }
-
- /*
- * Parse the given document as XRDS looking for OpenID consumer services.
- *
- * @return array of Auth_OpenID_ServiceEndpoint or null if the
- * document cannot be parsed.
- */
- function consumerFromXRDS($uri, $xrds_text)
- {
- $xrds =& Auth_Yadis_XRDS::parseXRDS($xrds_text);
-
- if ($xrds) {
- $yadis_services =
- $xrds->services(array('filter_MatchesAnyOpenIDConsumerType'));
- return Auth_OpenID_makeOpenIDEndpoints($uri, $yadis_services);
- }
-
- return null;
- }
-
- /*
- * Parse the given document as XRDS looking for OpenID services.
- *
- * @return array of Auth_OpenID_ServiceEndpoint or null if the
- * document cannot be parsed.
- */
- static function fromXRDS($uri, $xrds_text)
- {
- $xrds = Auth_Yadis_XRDS::parseXRDS($xrds_text);
-
- if ($xrds) {
- $yadis_services =
- $xrds->services(array('filter_MatchesAnyOpenIDType'));
- return Auth_OpenID_makeOpenIDEndpoints($uri, $yadis_services);
- }
-
- return null;
- }
-
- /*
- * Create endpoints from a DiscoveryResult.
- *
- * @param discoveryResult Auth_Yadis_DiscoveryResult
- * @return array of Auth_OpenID_ServiceEndpoint or null if
- * endpoints cannot be created.
- */
- static function fromDiscoveryResult($discoveryResult)
- {
- if ($discoveryResult->isXRDS()) {
- return Auth_OpenID_ServiceEndpoint::fromXRDS(
- $discoveryResult->normalized_uri,
- $discoveryResult->response_text);
- } else {
- return Auth_OpenID_ServiceEndpoint::fromHTML(
- $discoveryResult->normalized_uri,
- $discoveryResult->response_text);
- }
- }
-
- static function fromHTML($uri, $html)
- {
- $discovery_types = array(
- array(Auth_OpenID_TYPE_2_0,
- 'openid2.provider', 'openid2.local_id'),
- array(Auth_OpenID_TYPE_1_1,
- 'openid.server', 'openid.delegate')
- );
-
- $services = array();
-
- foreach ($discovery_types as $triple) {
- list($type_uri, $server_rel, $delegate_rel) = $triple;
-
- $urls = Auth_OpenID_legacy_discover($html, $server_rel,
- $delegate_rel);
-
- if ($urls === false) {
- continue;
- }
-
- list($delegate_url, $server_url) = $urls;
-
- $service = new Auth_OpenID_ServiceEndpoint();
- $service->claimed_id = $uri;
- $service->local_id = $delegate_url;
- $service->server_url = $server_url;
- $service->type_uris = array($type_uri);
-
- $services[] = $service;
- }
-
- return $services;
- }
-
- function copy()
- {
- $x = new Auth_OpenID_ServiceEndpoint();
-
- $x->claimed_id = $this->claimed_id;
- $x->server_url = $this->server_url;
- $x->type_uris = $this->type_uris;
- $x->local_id = $this->local_id;
- $x->canonicalID = $this->canonicalID;
- $x->used_yadis = $this->used_yadis;
-
- return $x;
- }
-}
-
-function Auth_OpenID_findOPLocalIdentifier($service, $type_uris)
-{
- // Extract a openid:Delegate value from a Yadis Service element.
- // If no delegate is found, returns null. Returns false on
- // discovery failure (when multiple delegate/localID tags have
- // different values).
-
- $service->parser->registerNamespace('openid',
- Auth_OpenID_XMLNS_1_0);
-
- $service->parser->registerNamespace('xrd',
- Auth_Yadis_XMLNS_XRD_2_0);
-
- $parser = $service->parser;
-
- $permitted_tags = array();
-
- if (in_array(Auth_OpenID_TYPE_1_1, $type_uris) ||
- in_array(Auth_OpenID_TYPE_1_0, $type_uris)) {
- $permitted_tags[] = 'openid:Delegate';
- }
-
- if (in_array(Auth_OpenID_TYPE_2_0, $type_uris)) {
- $permitted_tags[] = 'xrd:LocalID';
- }
-
- $local_id = null;
-
- foreach ($permitted_tags as $tag_name) {
- $tags = $service->getElements($tag_name);
-
- foreach ($tags as $tag) {
- $content = $parser->content($tag);
-
- if ($local_id === null) {
- $local_id = $content;
- } else if ($local_id != $content) {
- return false;
- }
- }
- }
-
- return $local_id;
-}
-
-function filter_MatchesAnyOpenIDType($service)
-{
- $uris = $service->getTypes();
-
- foreach ($uris as $uri) {
- if (in_array($uri, Auth_OpenID_getOpenIDTypeURIs())) {
- return true;
- }
- }
-
- return false;
-}
-
-function filter_MatchesAnyOpenIDConsumerType(&$service)
-{
- $uris = $service->getTypes();
-
- foreach ($uris as $uri) {
- if (in_array($uri, Auth_OpenID_getOpenIDConsumerTypeURIs())) {
- return true;
- }
- }
-
- return false;
-}
-
-function Auth_OpenID_bestMatchingService($service, $preferred_types)
-{
- // Return the index of the first matching type, or something
- // higher if no type matches.
- //
- // This provides an ordering in which service elements that
- // contain a type that comes earlier in the preferred types list
- // come before service elements that come later. If a service
- // element has more than one type, the most preferred one wins.
-
- foreach ($preferred_types as $index => $typ) {
- if (in_array($typ, $service->type_uris)) {
- return $index;
- }
- }
-
- return count($preferred_types);
-}
-
-function Auth_OpenID_arrangeByType($service_list, $preferred_types)
-{
- // Rearrange service_list in a new list so services are ordered by
- // types listed in preferred_types. Return the new list.
-
- // Build a list with the service elements in tuples whose
- // comparison will prefer the one with the best matching service
- $prio_services = array();
- foreach ($service_list as $index => $service) {
- $prio_services[] = array(Auth_OpenID_bestMatchingService($service,
- $preferred_types),
- $index, $service);
- }
-
- sort($prio_services);
-
- // Now that the services are sorted by priority, remove the sort
- // keys from the list.
- foreach ($prio_services as $index => $s) {
- $prio_services[$index] = $prio_services[$index][2];
- }
-
- return $prio_services;
-}
-
-// Extract OP Identifier services. If none found, return the rest,
-// sorted with most preferred first according to
-// OpenIDServiceEndpoint.openid_type_uris.
-//
-// openid_services is a list of OpenIDServiceEndpoint objects.
-//
-// Returns a list of OpenIDServiceEndpoint objects."""
-function Auth_OpenID_getOPOrUserServices($openid_services)
-{
- $op_services = Auth_OpenID_arrangeByType($openid_services,
- array(Auth_OpenID_TYPE_2_0_IDP));
-
- $openid_services = Auth_OpenID_arrangeByType($openid_services,
- Auth_OpenID_getOpenIDTypeURIs());
-
- if ($op_services) {
- return $op_services;
- } else {
- return $openid_services;
- }
-}
-
-function Auth_OpenID_makeOpenIDEndpoints($uri, $yadis_services)
-{
- $s = array();
-
- if (!$yadis_services) {
- return $s;
- }
-
- foreach ($yadis_services as $service) {
- $type_uris = $service->getTypes();
- $uris = $service->getURIs();
-
- // If any Type URIs match and there is an endpoint URI
- // specified, then this is an OpenID endpoint
- if ($type_uris &&
- $uris) {
- foreach ($uris as $service_uri) {
- $openid_endpoint = new Auth_OpenID_ServiceEndpoint();
- if ($openid_endpoint->parseService($uri,
- $service_uri,
- $type_uris,
- $service)) {
- $s[] = $openid_endpoint;
- }
- }
- }
- }
-
- return $s;
-}
-
-function Auth_OpenID_discoverWithYadis($uri, $fetcher,
- $endpoint_filter='Auth_OpenID_getOPOrUserServices',
- $discover_function=null)
-{
- // Discover OpenID services for a URI. Tries Yadis and falls back
- // on old-style <link rel='...'> discovery if Yadis fails.
-
- // Might raise a yadis.discover.DiscoveryFailure if no document
- // came back for that URI at all. I don't think falling back to
- // OpenID 1.0 discovery on the same URL will help, so don't bother
- // to catch it.
- if ($discover_function === null) {
- $discover_function = array('Auth_Yadis_Yadis', 'discover');
- }
-
- $openid_services = array();
-
- $response = call_user_func_array($discover_function,
- array($uri, $fetcher));
-
- $yadis_url = $response->normalized_uri;
- $yadis_services = array();
-
- if ($response->isFailure() && !$response->isXRDS()) {
- return array($uri, array());
- }
-
- $openid_services = Auth_OpenID_ServiceEndpoint::fromXRDS(
- $yadis_url,
- $response->response_text);
-
- if (!$openid_services) {
- if ($response->isXRDS()) {
- return Auth_OpenID_discoverWithoutYadis($uri,
- $fetcher);
- }
-
- // Try to parse the response as HTML to get OpenID 1.0/1.1
- // <link rel="...">
- $openid_services = Auth_OpenID_ServiceEndpoint::fromHTML(
- $yadis_url,
- $response->response_text);
- }
-
- $openid_services = call_user_func_array($endpoint_filter,
- array($openid_services));
-
- return array($yadis_url, $openid_services);
-}
-
-function Auth_OpenID_discoverURI($uri, $fetcher)
-{
- $uri = Auth_OpenID::normalizeUrl($uri);
- return Auth_OpenID_discoverWithYadis($uri, $fetcher);
-}
-
-function Auth_OpenID_discoverWithoutYadis($uri, $fetcher)
-{
- $http_resp = @$fetcher->get($uri);
-
- if ($http_resp->status != 200 and $http_resp->status != 206) {
- return array($uri, array());
- }
-
- $identity_url = $http_resp->final_url;
-
- // Try to parse the response as HTML to get OpenID 1.0/1.1 <link
- // rel="...">
- $openid_services = Auth_OpenID_ServiceEndpoint::fromHTML(
- $identity_url,
- $http_resp->body);
-
- return array($identity_url, $openid_services);
-}
-
-function Auth_OpenID_discoverXRI($iname, $fetcher)
-{
- $resolver = new Auth_Yadis_ProxyResolver($fetcher);
- list($canonicalID, $yadis_services) =
- $resolver->query($iname,
- Auth_OpenID_getOpenIDTypeURIs(),
- array('filter_MatchesAnyOpenIDType'));
-
- $openid_services = Auth_OpenID_makeOpenIDEndpoints($iname,
- $yadis_services);
-
- $openid_services = Auth_OpenID_getOPOrUserServices($openid_services);
-
- for ($i = 0; $i < count($openid_services); $i++) {
- $openid_services[$i]->canonicalID = $canonicalID;
- $openid_services[$i]->claimed_id = $canonicalID;
- $openid_services[$i]->display_identifier = $iname;
- }
-
- // FIXME: returned xri should probably be in some normal form
- return array($iname, $openid_services);
-}
-
-function Auth_OpenID_discover($uri, $fetcher)
-{
- // If the fetcher (i.e., PHP) doesn't support SSL, we can't do
- // discovery on an HTTPS URL.
- if ($fetcher->isHTTPS($uri) && !$fetcher->supportsSSL()) {
- return array($uri, array());
- }
-
- if (Auth_Yadis_identifierScheme($uri) == 'XRI') {
- $result = Auth_OpenID_discoverXRI($uri, $fetcher);
- } else {
- $result = Auth_OpenID_discoverURI($uri, $fetcher);
- }
-
- // If the fetcher doesn't support SSL, we can't interact with
- // HTTPS server URLs; remove those endpoints from the list.
- if (!$fetcher->supportsSSL()) {
- $http_endpoints = array();
- list($new_uri, $endpoints) = $result;
-
- foreach ($endpoints as $e) {
- if (!$fetcher->isHTTPS($e->server_url)) {
- $http_endpoints[] = $e;
- }
- }
-
- $result = array($new_uri, $http_endpoints);
- }
-
- return $result;
-}
-
-
-
--- a/lib/openid-php/Auth/OpenID/DumbStore.php
+++ /dev/null
@@ -1,100 +1,1 @@
-<?php
-/**
- * This file supplies a dumb store backend for OpenID servers and
- * consumers.
- *
- * PHP versions 4 and 5
- *
- * LICENSE: See the COPYING file included in this distribution.
- *
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-
-/**
- * Import the interface for creating a new store class.
- */
-require_once 'Auth/OpenID/Interface.php';
-require_once 'Auth/OpenID/HMAC.php';
-
-/**
- * This is a store for use in the worst case, when you have no way of
- * saving state on the consumer site. Using this store makes the
- * consumer vulnerable to replay attacks, as it's unable to use
- * nonces. Avoid using this store if it is at all possible.
- *
- * Most of the methods of this class are implementation details.
- * Users of this class need to worry only about the constructor.
- *
- * @package OpenID
- */
-class Auth_OpenID_DumbStore extends Auth_OpenID_OpenIDStore {
-
- /**
- * Creates a new {@link Auth_OpenID_DumbStore} instance. For the security
- * of the tokens generated by the library, this class attempts to
- * at least have a secure implementation of getAuthKey.
- *
- * When you create an instance of this class, pass in a secret
- * phrase. The phrase is hashed with sha1 to make it the correct
- * length and form for an auth key. That allows you to use a long
- * string as the secret phrase, which means you can make it very
- * difficult to guess.
- *
- * Each {@link Auth_OpenID_DumbStore} instance that is created for use by
- * your consumer site needs to use the same $secret_phrase.
- *
- * @param string secret_phrase The phrase used to create the auth
- * key returned by getAuthKey
- */
- function Auth_OpenID_DumbStore($secret_phrase)
- {
- $this->auth_key = Auth_OpenID_SHA1($secret_phrase);
- }
-
- /**
- * This implementation does nothing.
- */
- function storeAssociation($server_url, $association)
- {
- }
-
- /**
- * This implementation always returns null.
- */
- function getAssociation($server_url, $handle = null)
- {
- return null;
- }
-
- /**
- * This implementation always returns false.
- */
- function removeAssociation($server_url, $handle)
- {
- return false;
- }
-
- /**
- * In a system truly limited to dumb mode, nonces must all be
- * accepted. This therefore always returns true, which makes
- * replay attacks feasible.
- */
- function useNonce($server_url, $timestamp, $salt)
- {
- return true;
- }
-
- /**
- * This method returns the auth key generated by the constructor.
- */
- function getAuthKey()
- {
- return $this->auth_key;
- }
-}
-
-
--- a/lib/openid-php/Auth/OpenID/Extension.php
+++ /dev/null
@@ -1,62 +1,1 @@
-<?php
-/**
- * An interface for OpenID extensions.
- *
- * @package OpenID
- */
-
-/**
- * Require the Message implementation.
- */
-require_once 'Auth/OpenID/Message.php';
-
-/**
- * A base class for accessing extension request and response data for
- * the OpenID 2 protocol.
- *
- * @package OpenID
- */
-class Auth_OpenID_Extension {
- /**
- * ns_uri: The namespace to which to add the arguments for this
- * extension
- */
- var $ns_uri = null;
- var $ns_alias = null;
-
- /**
- * Get the string arguments that should be added to an OpenID
- * message for this extension.
- */
- function getExtensionArgs()
- {
- return null;
- }
-
- /**
- * Add the arguments from this extension to the provided message.
- *
- * Returns the message with the extension arguments added.
- */
- function toMessage($message)
- {
- $implicit = $message->isOpenID1();
- $added = $message->namespaces->addAlias($this->ns_uri,
- $this->ns_alias,
- $implicit);
-
- if ($added === null) {
- if ($message->namespaces->getAlias($this->ns_uri) !=
- $this->ns_alias) {
- return null;
- }
- }
-
- $message->updateArgs($this->ns_uri,
- $this->getExtensionArgs());
- return $message;
- }
-}
-
-
--- a/lib/openid-php/Auth/OpenID/FileStore.php
+++ /dev/null
@@ -1,619 +1,1 @@
-<?php
-/**
- * This file supplies a Memcached store backend for OpenID servers and
- * consumers.
- *
- * PHP versions 4 and 5
- *
- * LICENSE: See the COPYING file included in this distribution.
- *
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-
-/**
- * Require base class for creating a new interface.
- */
-require_once 'Auth/OpenID.php';
-require_once 'Auth/OpenID/Interface.php';
-require_once 'Auth/OpenID/HMAC.php';
-require_once 'Auth/OpenID/Nonce.php';
-
-/**
- * This is a filesystem-based store for OpenID associations and
- * nonces. This store should be safe for use in concurrent systems on
- * both windows and unix (excluding NFS filesystems). There are a
- * couple race conditions in the system, but those failure cases have
- * been set up in such a way that the worst-case behavior is someone
- * having to try to log in a second time.
- *
- * Most of the methods of this class are implementation details.
- * People wishing to just use this store need only pay attention to
- * the constructor.
- *
- * @package OpenID
- */
-class Auth_OpenID_FileStore extends Auth_OpenID_OpenIDStore {
-
- /**
- * Initializes a new {@link Auth_OpenID_FileStore}. This
- * initializes the nonce and association directories, which are
- * subdirectories of the directory passed in.
- *
- * @param string $directory This is the directory to put the store
- * directories in.
- */
- function Auth_OpenID_FileStore($directory)
- {
- if (!Auth_OpenID::ensureDir($directory)) {
- trigger_error('Not a directory and failed to create: '
- . $directory, E_USER_ERROR);
- }
- $directory = realpath($directory);
-
- $this->directory = $directory;
- $this->active = true;
-
- $this->nonce_dir = $directory . DIRECTORY_SEPARATOR . 'nonces';
-
- $this->association_dir = $directory . DIRECTORY_SEPARATOR .
- 'associations';
-
- // Temp dir must be on the same filesystem as the assciations
- // $directory.
- $this->temp_dir = $directory . DIRECTORY_SEPARATOR . 'temp';
-
- $this->max_nonce_age = 6 * 60 * 60; // Six hours, in seconds
-
- if (!$this->_setup()) {
- trigger_error('Failed to initialize OpenID file store in ' .
- $directory, E_USER_ERROR);
- }
- }
-
- function destroy()
- {
- Auth_OpenID_FileStore::_rmtree($this->directory);
- $this->active = false;
- }
-
- /**
- * Make sure that the directories in which we store our data
- * exist.
- *
- * @access private
- */
- function _setup()
- {
- return (Auth_OpenID::ensureDir($this->nonce_dir) &&
- Auth_OpenID::ensureDir($this->association_dir) &&
- Auth_OpenID::ensureDir($this->temp_dir));
- }
-
- /**
- * Create a temporary file on the same filesystem as
- * $this->association_dir.
- *
- * The temporary directory should not be cleaned if there are any
- * processes using the store. If there is no active process using
- * the store, it is safe to remove all of the files in the
- * temporary directory.
- *
- * @return array ($fd, $filename)
- * @access private
- */
- function _mktemp()
- {
- $name = Auth_OpenID_FileStore::_mkstemp($dir = $this->temp_dir);
- $file_obj = @fopen($name, 'wb');
- if ($file_obj !== false) {
- return array($file_obj, $name);
- } else {
- Auth_OpenID_FileStore::_removeIfPresent($name);
- }
- }
-
- function cleanupNonces()
- {
- global $Auth_OpenID_SKEW;
-
- $nonces = Auth_OpenID_FileStore::_listdir($this->nonce_dir);
- $now = time();
-
- $removed = 0;
- // Check all nonces for expiry
- foreach ($nonces as $nonce_fname) {
- $base = basename($nonce_fname);
- $parts = explode('-', $base, 2);
- $timestamp = $parts[0];
- $timestamp = intval($timestamp, 16);
- if (abs($timestamp - $now) > $Auth_OpenID_SKEW) {
- Auth_OpenID_FileStore::_removeIfPresent($nonce_fname);
- $removed += 1;
- }
- }
- return $removed;
- }
-
- /**
- * Create a unique filename for a given server url and
- * handle. This implementation does not assume anything about the
- * format of the handle. The filename that is returned will
- * contain the domain name from the server URL for ease of human
- * inspection of the data directory.
- *
- * @return string $filename
- */
- function getAssociationFilename($server_url, $handle)
- {
- if (!$this->active) {
- trigger_error("FileStore no longer active", E_USER_ERROR);
- return null;
- }
-
- if (strpos($server_url, '://') === false) {
- trigger_error(sprintf("Bad server URL: %s", $server_url),
- E_USER_WARNING);
- return null;
- }
-
- list($proto, $rest) = explode('://', $server_url, 2);
- $parts = explode('/', $rest);
- $domain = Auth_OpenID_FileStore::_filenameEscape($parts[0]);
- $url_hash = Auth_OpenID_FileStore::_safe64($server_url);
- if ($handle) {
- $handle_hash = Auth_OpenID_FileStore::_safe64($handle);
- } else {
- $handle_hash = '';
- }
-
- $filename = sprintf('%s-%s-%s-%s', $proto, $domain, $url_hash,
- $handle_hash);
-
- return $this->association_dir. DIRECTORY_SEPARATOR . $filename;
- }
-
- /**
- * Store an association in the association directory.
- */
- function storeAssociation($server_url, $association)
- {
- if (!$this->active) {
- trigger_error("FileStore no longer active", E_USER_ERROR);
- return false;
- }
-
- $association_s = $association->serialize();
- $filename = $this->getAssociationFilename($server_url,
- $association->handle);
- list($tmp_file, $tmp) = $this->_mktemp();
-
- if (!$tmp_file) {
- trigger_error("_mktemp didn't return a valid file descriptor",
- E_USER_WARNING);
- return false;
- }
-
- fwrite($tmp_file, $association_s);
-
- fflush($tmp_file);
-
- fclose($tmp_file);
-
- if (@rename($tmp, $filename)) {
- return true;
- } else {
- // In case we are running on Windows, try unlinking the
- // file in case it exists.
- @unlink($filename);
-
- // Now the target should not exist. Try renaming again,
- // giving up if it fails.
- if (@rename($tmp, $filename)) {
- return true;
- }
- }
-
- // If there was an error, don't leave the temporary file
- // around.
- Auth_OpenID_FileStore::_removeIfPresent($tmp);
- return false;
- }
-
- /**
- * Retrieve an association. If no handle is specified, return the
- * association with the most recent issue time.
- *
- * @return mixed $association
- */
- function getAssociation($server_url, $handle = null)
- {
- if (!$this->active) {
- trigger_error("FileStore no longer active", E_USER_ERROR);
- return null;
- }
-
- if ($handle === null) {
- $handle = '';
- }
-
- // The filename with the empty handle is a prefix of all other
- // associations for the given server URL.
- $filename = $this->getAssociationFilename($server_url, $handle);
-
- if ($handle) {
- return $this->_getAssociation($filename);
- } else {
- $association_files =
- Auth_OpenID_FileStore::_listdir($this->association_dir);
- $matching_files = array();
-
- // strip off the path to do the comparison
- $name = basename($filename);
- foreach ($association_files as $association_file) {
- $base = basename($association_file);
- if (strpos($base, $name) === 0) {
- $matching_files[] = $association_file;
- }
- }
-
- $matching_associations = array();
- // read the matching files and sort by time issued
- foreach ($matching_files as $full_name) {
- $association = $this->_getAssociation($full_name);
- if ($association !== null) {
- $matching_associations[] = array($association->issued,
- $association);
- }
- }
-
- $issued = array();
- $assocs = array();
- foreach ($matching_associations as $key => $assoc) {
- $issued[$key] = $assoc[0];
- $assocs[$key] = $assoc[1];
- }
-
- array_multisort($issued, SORT_DESC, $assocs, SORT_DESC,
- $matching_associations);
-
- // return the most recently issued one.
- if ($matching_associations) {
- list($issued, $assoc) = $matching_associations[0];
- return $assoc;
- } else {
- return null;
- }
- }
- }
-
- /**
- * @access private
- */
- function _getAssociation($filename)
- {
- if (!$this->active) {
- trigger_error("FileStore no longer active", E_USER_ERROR);
- return null;
- }
-
- $assoc_file = @fopen($filename, 'rb');
-
- if ($assoc_file === false) {
- return null;
- }
-
- $assoc_s = fread($assoc_file, filesize($filename));
- fclose($assoc_file);
-
- if (!$assoc_s) {
- return null;
- }
-
- $association =
- Auth_OpenID_Association::deserialize('Auth_OpenID_Association',
- $assoc_s);
-
- if (!$association) {
- Auth_OpenID_FileStore::_removeIfPresent($filename);
- return null;
- }
-
- if ($association->getExpiresIn() == 0) {
- Auth_OpenID_FileStore::_removeIfPresent($filename);
- return null;
- } else {
- return $association;
- }
- }
-
- /**
- * Remove an association if it exists. Do nothing if it does not.
- *
- * @return bool $success
- */
- function removeAssociation($server_url, $handle)
- {
- if (!$this->active) {
- trigger_error("FileStore no longer active", E_USER_ERROR);
- return null;
- }
-
- $assoc = $this->getAssociation($server_url, $handle);
- if ($assoc === null) {
- return false;
- } else {
- $filename = $this->getAssociationFilename($server_url, $handle);
- return Auth_OpenID_FileStore::_removeIfPresent($filename);
- }
- }
-
- /**
- * Return whether this nonce is present. As a side effect, mark it
- * as no longer present.
- *
- * @return bool $present
- */
- function useNonce($server_url, $timestamp, $salt)
- {
- global $Auth_OpenID_SKEW;
-
- if (!$this->active) {
- trigger_error("FileStore no longer active", E_USER_ERROR);
- return null;
- }
-
- if ( abs($timestamp - time()) > $Auth_OpenID_SKEW ) {
- return false;
- }
-
- if ($server_url) {
- list($proto, $rest) = explode('://', $server_url, 2);
- } else {
- $proto = '';
- $rest = '';
- }
-
- $parts = explode('/', $rest, 2);
- $domain = $this->_filenameEscape($parts[0]);
- $url_hash = $this->_safe64($server_url);
- $salt_hash = $this->_safe64($salt);
-
- $filename = sprintf('%08x-%s-%s-%s-%s', $timestamp, $proto,
- $domain, $url_hash, $salt_hash);
- $filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $filename;
-
- $result = @fopen($filename, 'x');
-
- if ($result === false) {
- return false;
- } else {
- fclose($result);
- return true;
- }
- }
-
- /**
- * Remove expired entries from the database. This is potentially
- * expensive, so only run when it is acceptable to take time.
- *
- * @access private
- */
- function _allAssocs()
- {
- $all_associations = array();
-
- $association_filenames =
- Auth_OpenID_FileStore::_listdir($this->association_dir);
-
- foreach ($association_filenames as $association_filename) {
- $association_file = fopen($association_filename, 'rb');
-
- if ($association_file !== false) {
- $assoc_s = fread($association_file,
- filesize($association_filename));
- fclose($association_file);
-
- // Remove expired or corrupted associations
- $association =
- Auth_OpenID_Association::deserialize(
- 'Auth_OpenID_Association', $assoc_s);
-
- if ($association === null) {
- Auth_OpenID_FileStore::_removeIfPresent(
- $association_filename);
- } else {
- if ($association->getExpiresIn() == 0) {
- $all_associations[] = array($association_filename,
- $association);
- }
- }
- }
- }
-
- return $all_associations;
- }
-
- function clean()
- {
- if (!$this->active) {
- trigger_error("FileStore no longer active", E_USER_ERROR);
- return null;
- }
-
- $nonces = Auth_OpenID_FileStore::_listdir($this->nonce_dir);
- $now = time();
-
- // Check all nonces for expiry
- foreach ($nonces as $nonce) {
- if (!Auth_OpenID_checkTimestamp($nonce, $now)) {
- $filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $nonce;
- Auth_OpenID_FileStore::_removeIfPresent($filename);
- }
- }
-
- foreach ($this->_allAssocs() as $pair) {
- list($assoc_filename, $assoc) = $pair;
- if ($assoc->getExpiresIn() == 0) {
- Auth_OpenID_FileStore::_removeIfPresent($assoc_filename);
- }
- }
- }
-
- /**
- * @access private
- */
- function _rmtree($dir)
- {
- if ($dir[strlen($dir) - 1] != DIRECTORY_SEPARATOR) {
- $dir .= DIRECTORY_SEPARATOR;
- }
-
- if ($handle = opendir($dir)) {
- while ($item = readdir($handle)) {
- if (!in_array($item, array('.', '..'))) {
- if (is_dir($dir . $item)) {
-
- if (!Auth_OpenID_FileStore::_rmtree($dir . $item)) {
- return false;
- }
- } else if (is_file($dir . $item)) {
- if (!unlink($dir . $item)) {
- return false;
- }
- }
- }
- }
-
- closedir($handle);
-
- if (!@rmdir($dir)) {
- return false;
- }
-
- return true;
- } else {
- // Couldn't open directory.
- return false;
- }
- }
-
- /**
- * @access private
- */
- function _mkstemp($dir)
- {
- foreach (range(0, 4) as $i) {
- $name = tempnam($dir, "php_openid_filestore_");
-
- if ($name !== false) {
- return $name;
- }
- }
- return false;
- }
-
- /**
- * @access private
- */
- static function _mkdtemp($dir)
- {
- foreach (range(0, 4) as $i) {
- $name = $dir . strval(DIRECTORY_SEPARATOR) . strval(getmypid()) .
- "-" . strval(rand(1, time()));
- if (!mkdir($name, 0700)) {
- return false;
- } else {
- return $name;
- }
- }
- return false;
- }
-
- /**
- * @access private
- */
- function _listdir($dir)
- {
- $handle = opendir($dir);
- $files = array();
- while (false !== ($filename = readdir($handle))) {
- if (!in_array($filename, array('.', '..'))) {
- $files[] = $dir . DIRECTORY_SEPARATOR . $filename;
- }
- }
- return $files;
- }
-
- /**
- * @access private
- */
- function _isFilenameSafe($char)
- {
- $_Auth_OpenID_filename_allowed = Auth_OpenID_letters .
- Auth_OpenID_digits . ".";
- return (strpos($_Auth_OpenID_filename_allowed, $char) !== false);
- }
-
- /**
- * @access private
- */
- function _safe64($str)
- {
- $h64 = base64_encode(Auth_OpenID_SHA1($str));
- $h64 = str_replace('+', '_', $h64);
- $h64 = str_replace('/', '.', $h64);
- $h64 = str_replace('=', '', $h64);
- return $h64;
- }
-
- /**
- * @access private
- */
- function _filenameEscape($str)
- {
- $filename = "";
- $b = Auth_OpenID::toBytes($str);
-
- for ($i = 0; $i < count($b); $i++) {
- $c = $b[$i];
- if (Auth_OpenID_FileStore::_isFilenameSafe($c)) {
- $filename .= $c;
- } else {
- $filename .= sprintf("_%02X", ord($c));
- }
- }
- return $filename;
- }
-
- /**
- * Attempt to remove a file, returning whether the file existed at
- * the time of the call.
- *
- * @access private
- * @return bool $result True if the file was present, false if not.
- */
- function _removeIfPresent($filename)
- {
- return @unlink($filename);
- }
-
- function cleanupAssociations()
- {
- $removed = 0;
- foreach ($this->_allAssocs() as $pair) {
- list($assoc_filename, $assoc) = $pair;
- if ($assoc->getExpiresIn() == 0) {
- $this->_removeIfPresent($assoc_filename);
- $removed += 1;
- }
- }
- return $removed;
- }
-}
-
-
-
--- a/lib/openid-php/Auth/OpenID/HMAC.php
+++ /dev/null
@@ -1,106 +1,1 @@
-<?php
-/**
- * This is the HMACSHA1 implementation for the OpenID library.
- *
- * PHP versions 4 and 5
- *
- * LICENSE: See the COPYING file included in this distribution.
- *
- * @access private
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-
-require_once 'Auth/OpenID.php';
-
-/**
- * SHA1_BLOCKSIZE is this module's SHA1 blocksize used by the fallback
- * implementation.
- */
-define('Auth_OpenID_SHA1_BLOCKSIZE', 64);
-
-function Auth_OpenID_SHA1($text)
-{
- if (function_exists('hash') &&
- function_exists('hash_algos') &&
- (in_array('sha1', hash_algos()))) {
- // PHP 5 case (sometimes): 'hash' available and 'sha1' algo
- // supported.
- return hash('sha1', $text, true);
- } else if (function_exists('sha1')) {
- // PHP 4 case: 'sha1' available.
- $hex = sha1($text);
- $raw = '';
- for ($i = 0; $i < 40; $i += 2) {
- $hexcode = substr($hex, $i, 2);
- $charcode = (int)base_convert($hexcode, 16, 10);
- $raw .= chr($charcode);
- }
- return $raw;
- } else {
- // Explode.
- trigger_error('No SHA1 function found', E_USER_ERROR);
- }
-}
-
-/**
- * Compute an HMAC/SHA1 hash.
- *
- * @access private
- * @param string $key The HMAC key
- * @param string $text The message text to hash
- * @return string $mac The MAC
- */
-function Auth_OpenID_HMACSHA1($key, $text)
-{
- if (Auth_OpenID::bytes($key) > Auth_OpenID_SHA1_BLOCKSIZE) {
- $key = Auth_OpenID_SHA1($key, true);
- }
-
- if (function_exists('hash_hmac') &&
- function_exists('hash_algos') &&
- (in_array('sha1', hash_algos()))) {
- return hash_hmac('sha1', $text, $key, true);
- }
- // Home-made solution
-
- $key = str_pad($key, Auth_OpenID_SHA1_BLOCKSIZE, chr(0x00));
- $ipad = str_repeat(chr(0x36), Auth_OpenID_SHA1_BLOCKSIZE);
- $opad = str_repeat(chr(0x5c), Auth_OpenID_SHA1_BLOCKSIZE);
- $hash1 = Auth_OpenID_SHA1(($key ^ $ipad) . $text, true);
- $hmac = Auth_OpenID_SHA1(($key ^ $opad) . $hash1, true);
- return $hmac;
-}
-
-if (function_exists('hash') &&
- function_exists('hash_algos') &&
- (in_array('sha256', hash_algos()))) {
- function Auth_OpenID_SHA256($text)
- {
- // PHP 5 case: 'hash' available and 'sha256' algo supported.
- return hash('sha256', $text, true);
- }
- define('Auth_OpenID_SHA256_SUPPORTED', true);
-} else {
- define('Auth_OpenID_SHA256_SUPPORTED', false);
-}
-
-if (function_exists('hash_hmac') &&
- function_exists('hash_algos') &&
- (in_array('sha256', hash_algos()))) {
-
- function Auth_OpenID_HMACSHA256($key, $text)
- {
- // Return raw MAC (not hex string).
- return hash_hmac('sha256', $text, $key, true);
- }
-
- define('Auth_OpenID_HMACSHA256_SUPPORTED', true);
-} else {
- define('Auth_OpenID_HMACSHA256_SUPPORTED', false);
-}
-
-
--- a/lib/openid-php/Auth/OpenID/Interface.php
+++ /dev/null
@@ -1,197 +1,1 @@
-<?php
-/**
- * This file specifies the interface for PHP OpenID store implementations.
- *
- * PHP versions 4 and 5
- *
- * LICENSE: See the COPYING file included in this distribution.
- *
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-
-/**
- * This is the interface for the store objects the OpenID library
- * uses. It is a single class that provides all of the persistence
- * mechanisms that the OpenID library needs, for both servers and
- * consumers. If you want to create an SQL-driven store, please see
- * then {@link Auth_OpenID_SQLStore} class.
- *
- * Change: Version 2.0 removed the storeNonce, getAuthKey, and isDumb
- * methods, and changed the behavior of the useNonce method to support
- * one-way nonces.
- *
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- */
-class Auth_OpenID_OpenIDStore {
- /**
- * This method puts an Association object into storage,
- * retrievable by server URL and handle.
- *
- * @param string $server_url The URL of the identity server that
- * this association is with. Because of the way the server portion
- * of the library uses this interface, don't assume there are any
- * limitations on the character set of the input string. In
- * particular, expect to see unescaped non-url-safe characters in
- * the server_url field.
- *
- * @param Association $association The Association to store.
- */
- function storeAssociation($server_url, $association)
- {
- trigger_error("Auth_OpenID_OpenIDStore::storeAssociation ".
- "not implemented", E_USER_ERROR);
- }
-
- /*
- * Remove expired nonces from the store.
- *
- * Discards any nonce from storage that is old enough that its
- * timestamp would not pass useNonce().
- *
- * This method is not called in the normal operation of the
- * library. It provides a way for store admins to keep their
- * storage from filling up with expired data.
- *
- * @return the number of nonces expired
- */
- function cleanupNonces()
- {
- trigger_error("Auth_OpenID_OpenIDStore::cleanupNonces ".
- "not implemented", E_USER_ERROR);
- }
-
- /*
- * Remove expired associations from the store.
- *
- * This method is not called in the normal operation of the
- * library. It provides a way for store admins to keep their
- * storage from filling up with expired data.
- *
- * @return the number of associations expired.
- */
- function cleanupAssociations()
- {
- trigger_error("Auth_OpenID_OpenIDStore::cleanupAssociations ".
- "not implemented", E_USER_ERROR);
- }
-
- /*
- * Shortcut for cleanupNonces(), cleanupAssociations().
- *
- * This method is not called in the normal operation of the
- * library. It provides a way for store admins to keep their
- * storage from filling up with expired data.
- */
- function cleanup()
- {
- return array($this->cleanupNonces(),
- $this->cleanupAssociations());
- }
-
- /**
- * Report whether this storage supports cleanup
- */
- function supportsCleanup()
- {
- return true;
- }
-
- /**
- * This method returns an Association object from storage that
- * matches the server URL and, if specified, handle. It returns
- * null if no such association is found or if the matching
- * association is expired.
- *
- * If no handle is specified, the store may return any association
- * which matches the server URL. If multiple associations are
- * valid, the recommended return value for this method is the one
- * most recently issued.
- *
- * This method is allowed (and encouraged) to garbage collect
- * expired associations when found. This method must not return
- * expired associations.
- *
- * @param string $server_url The URL of the identity server to get
- * the association for. Because of the way the server portion of
- * the library uses this interface, don't assume there are any
- * limitations on the character set of the input string. In
- * particular, expect to see unescaped non-url-safe characters in
- * the server_url field.
- *
- * @param mixed $handle This optional parameter is the handle of
- * the specific association to get. If no specific handle is
- * provided, any valid association matching the server URL is
- * returned.
- *
- * @return Association The Association for the given identity
- * server.
- */
- function getAssociation($server_url, $handle = null)
- {
- trigger_error("Auth_OpenID_OpenIDStore::getAssociation ".
- "not implemented", E_USER_ERROR);
- }
-
- /**
- * This method removes the matching association if it's found, and
- * returns whether the association was removed or not.
- *
- * @param string $server_url The URL of the identity server the
- * association to remove belongs to. Because of the way the server
- * portion of the library uses this interface, don't assume there
- * are any limitations on the character set of the input
- * string. In particular, expect to see unescaped non-url-safe
- * characters in the server_url field.
- *
- * @param string $handle This is the handle of the association to
- * remove. If there isn't an association found that matches both
- * the given URL and handle, then there was no matching handle
- * found.
- *
- * @return mixed Returns whether or not the given association existed.
- */
- function removeAssociation($server_url, $handle)
- {
- trigger_error("Auth_OpenID_OpenIDStore::removeAssociation ".
- "not implemented", E_USER_ERROR);
- }
-
- /**
- * Called when using a nonce.
- *
- * This method should return C{True} if the nonce has not been
- * used before, and store it for a while to make sure nobody
- * tries to use the same value again. If the nonce has already
- * been used, return C{False}.
- *
- * Change: In earlier versions, round-trip nonces were used and a
- * nonce was only valid if it had been previously stored with
- * storeNonce. Version 2.0 uses one-way nonces, requiring a
- * different implementation here that does not depend on a
- * storeNonce call. (storeNonce is no longer part of the
- * interface.
- *
- * @param string $nonce The nonce to use.
- *
- * @return bool Whether or not the nonce was valid.
- */
- function useNonce($server_url, $timestamp, $salt)
- {
- trigger_error("Auth_OpenID_OpenIDStore::useNonce ".
- "not implemented", E_USER_ERROR);
- }
-
- /**
- * Removes all entries from the store; implementation is optional.
- */
- function reset()
- {
- }
-
-}
-
--- a/lib/openid-php/Auth/OpenID/KVForm.php
+++ /dev/null
@@ -1,112 +1,1 @@
-<?php
-/**
- * OpenID protocol key-value/comma-newline format parsing and
- * serialization
- *
- * PHP versions 4 and 5
- *
- * LICENSE: See the COPYING file included in this distribution.
- *
- * @access private
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-
-/**
- * Container for key-value/comma-newline OpenID format and parsing
- */
-class Auth_OpenID_KVForm {
- /**
- * Convert an OpenID colon/newline separated string into an
- * associative array
- *
- * @static
- * @access private
- */
- static function toArray($kvs, $strict=false)
- {
- $lines = explode("\n", $kvs);
-
- $last = array_pop($lines);
- if ($last !== '') {
- array_push($lines, $last);
- if ($strict) {
- return false;
- }
- }
-
- $values = array();
-
- for ($lineno = 0; $lineno < count($lines); $lineno++) {
- $line = $lines[$lineno];
- $kv = explode(':', $line, 2);
- if (count($kv) != 2) {
- if ($strict) {
- return false;
- }
- continue;
- }
-
- $key = $kv[0];
- $tkey = trim($key);
- if ($tkey != $key) {
- if ($strict) {
- return false;
- }
- }
-
- $value = $kv[1];
- $tval = trim($value);
- if ($tval != $value) {
- if ($strict) {
- return false;
- }
- }
-
- $values[$tkey] = $tval;
- }
-
- return $values;
- }
-
- /**
- * Convert an array into an OpenID colon/newline separated string
- *
- * @static
- * @access private
- */
- static function fromArray($values)
- {
- if ($values === null) {
- return null;
- }
-
- ksort($values);
-
- $serialized = '';
- foreach ($values as $key => $value) {
- if (is_array($value)) {
- list($key, $value) = array($value[0], $value[1]);
- }
-
- if (strpos($key, ':') !== false) {
- return null;
- }
-
- if (strpos($key, "\n") !== false) {
- return null;
- }
-
- if (strpos($value, "\n") !== false) {
- return null;
- }
- $serialized .= "$key:$value\n";
- }
- return $serialized;
- }
-}
-
-
--- a/lib/openid-php/Auth/OpenID/MDB2Store.php
+++ /dev/null
@@ -1,414 +1,1 @@
-<?php
-/**
- * SQL-backed OpenID stores for use with PEAR::MDB2.
- *
- * PHP versions 4 and 5
- *
- * LICENSE: See the COPYING file included in this distribution.
- *
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005 Janrain, Inc.
- * @license http://www.gnu.org/copyleft/lesser.html LGPL
- */
-
-require_once 'MDB2.php';
-
-/**
- * @access private
- */
-require_once 'Auth/OpenID/Interface.php';
-
-/**
- * @access private
- */
-require_once 'Auth/OpenID.php';
-
-/**
- * @access private
- */
-require_once 'Auth/OpenID/Nonce.php';
-
-/**
- * This store uses a PEAR::MDB2 connection to store persistence
- * information.
- *
- * The table names used are determined by the class variables
- * associations_table_name and nonces_table_name. To change the name
- * of the tables used, pass new table names into the constructor.
- *
- * To create the tables with the proper schema, see the createTables
- * method.
- *
- * @package OpenID
- */
-class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
- /**
- * This creates a new MDB2Store instance. It requires an
- * established database connection be given to it, and it allows
- * overriding the default table names.
- *
- * @param connection $connection This must be an established
- * connection to a database of the correct type for the SQLStore
- * subclass you're using. This must be a PEAR::MDB2 connection
- * handle.
- *
- * @param associations_table: This is an optional parameter to
- * specify the name of the table used for storing associations.
- * The default value is 'oid_associations'.
- *
- * @param nonces_table: This is an optional parameter to specify
- * the name of the table used for storing nonces. The default
- * value is 'oid_nonces'.
- */
- function Auth_OpenID_MDB2Store($connection,
- $associations_table = null,
- $nonces_table = null)
- {
- $this->associations_table_name = "oid_associations";
- $this->nonces_table_name = "oid_nonces";
-
- // Check the connection object type to be sure it's a PEAR
- // database connection.
- if (!is_object($connection) ||
- !is_subclass_of($connection, 'mdb2_driver_common')) {
- trigger_error("Auth_OpenID_MDB2Store expected PEAR connection " .
- "object (got ".get_class($connection).")",
- E_USER_ERROR);
- return;
- }
-
- $this->connection = $connection;
-
- // Be sure to set the fetch mode so the results are keyed on
- // column name instead of column index.
- $this->connection->setFetchMode(MDB2_FETCHMODE_ASSOC);
-
- if (PEAR::isError($this->connection->loadModule('Extended'))) {
- trigger_error("Unable to load MDB2_Extended module", E_USER_ERROR);
- return;
- }
-
- if ($associations_table) {
- $this->associations_table_name = $associations_table;
- }
-
- if ($nonces_table) {
- $this->nonces_table_name = $nonces_table;
- }
-
- $this->max_nonce_age = 6 * 60 * 60;
- }
-
- function tableExists($table_name)
- {
- return !PEAR::isError($this->connection->query(
- sprintf("SELECT * FROM %s LIMIT 0",
- $table_name)));
- }
-
- function createTables()
- {
- $n = $this->create_nonce_table();
- $a = $this->create_assoc_table();
-
- if (!$n || !$a) {
- return false;
- }
- return true;
- }
-
- function create_nonce_table()
- {
- if (!$this->tableExists($this->nonces_table_name)) {
- switch ($this->connection->phptype) {
- case "mysql":
- case "mysqli":
- // Custom SQL for MySQL to use InnoDB and variable-
- // length keys
- $r = $this->connection->exec(
- sprintf("CREATE TABLE %s (\n".
- " server_url VARCHAR(2047) NOT NULL DEFAULT '',\n".
- " timestamp INTEGER NOT NULL,\n".
- " salt CHAR(40) NOT NULL,\n".
- " UNIQUE (server_url(255), timestamp, salt)\n".
- ") TYPE=InnoDB",
- $this->nonces_table_name));
- if (PEAR::isError($r)) {
- return false;
- }
- break;
- default:
- if (PEAR::isError(
- $this->connection->loadModule('Manager'))) {
- return false;
- }
- $fields = array(
- "server_url" => array(
- "type" => "text",
- "length" => 2047,
- "notnull" => true
- ),
- "timestamp" => array(
- "type" => "integer",
- "notnull" => true
- ),
- "salt" => array(
- "type" => "text",
- "length" => 40,
- "fixed" => true,
- "notnull" => true
- )
- );
- $constraint = array(
- "unique" => 1,
- "fields" => array(
- "server_url" => true,
- "timestamp" => true,
- "salt" => true
- )
- );
-
- $r = $this->connection->createTable($this->nonces_table_name,
- $fields);
- if (PEAR::isError($r)) {
- return false;
- }
-
- $r = $this->connection->createConstraint(
- $this->nonces_table_name,
- $this->nonces_table_name . "_constraint",
- $constraint);
- if (PEAR::isError($r)) {
- return false;
- }
- break;
- }
- }
- return true;
- }
-
- function create_assoc_table()
- {
- if (!$this->tableExists($this->associations_table_name)) {
- switch ($this->connection->phptype) {
- case "mysql":
- case "mysqli":
- // Custom SQL for MySQL to use InnoDB and variable-
- // length keys
- $r = $this->connection->exec(
- sprintf("CREATE TABLE %s(\n".
- " server_url VARCHAR(2047) NOT NULL DEFAULT '',\n".
- " handle VARCHAR(255) NOT NULL,\n".
- " secret BLOB NOT NULL,\n".
- " issued INTEGER NOT NULL,\n".
- " lifetime INTEGER NOT NULL,\n".
- " assoc_type VARCHAR(64) NOT NULL,\n".
- " PRIMARY KEY (server_url(255), handle)\n".
- ") TYPE=InnoDB",
- $this->associations_table_name));
- if (PEAR::isError($r)) {
- return false;
- }
- break;
- default:
- if (PEAR::isError(
- $this->connection->loadModule('Manager'))) {
- return false;
- }
- $fields = array(
- "server_url" => array(
- "type" => "text",
- "length" => 2047,
- "notnull" => true
- ),
- "handle" => array(
- "type" => "text",
- "length" => 255,
- "notnull" => true
- ),
- "secret" => array(
- "type" => "blob",
- "length" => "255",
- "notnull" => true
- ),
- "issued" => array(
- "type" => "integer",
- "notnull" => true
- ),
- "lifetime" => array(
- "type" => "integer",
- "notnull" => true
- ),
- "assoc_type" => array(
- "type" => "text",
- "length" => 64,
- "notnull" => true
- )
- );
- $options = array(
- "primary" => array(
- "server_url" => true,
- "handle" => true
- )
- );
-
- $r = $this->connection->createTable(
- $this->associations_table_name,
- $fields,
- $options);
- if (PEAR::isError($r)) {
- return false;
- }
- break;
- }
- }
- return true;
- }
-
- function storeAssociation($server_url, $association)
- {
- $fields = array(
- "server_url" => array(
- "value" => $server_url,
- "key" => true
- ),
- "handle" => array(
- "value" => $association->handle,
- "key" => true
- ),
- "secret" => array(
- "value" => $association->secret,
- "type" => "blob"
- ),
- "issued" => array(
- "value" => $association->issued
- ),
- "lifetime" => array(
- "value" => $association->lifetime
- ),
- "assoc_type" => array(
- "value" => $association->assoc_type
- )
- );
-
- return !PEAR::isError($this->connection->replace(
- $this->associations_table_name,
- $fields));
- }
-
- function cleanupNonces()
- {
- global $Auth_OpenID_SKEW;
- $v = time() - $Auth_OpenID_SKEW;
-
- return $this->connection->exec(
- sprintf("DELETE FROM %s WHERE timestamp < %d",
- $this->nonces_table_name, $v));
- }
-
- function cleanupAssociations()
- {
- return $this->connection->exec(
- sprintf("DELETE FROM %s WHERE issued + lifetime < %d",
- $this->associations_table_name, time()));
- }
-
- function getAssociation($server_url, $handle = null)
- {
- $sql = "";
- $params = null;
- $types = array(
- "text",
- "blob",
- "integer",
- "integer",
- "text"
- );
- if ($handle !== null) {
- $sql = sprintf("SELECT handle, secret, issued, lifetime, assoc_type " .
- "FROM %s WHERE server_url = ? AND handle = ?",
- $this->associations_table_name);
- $params = array($server_url, $handle);
- } else {
- $sql = sprintf("SELECT handle, secret, issued, lifetime, assoc_type " .
- "FROM %s WHERE server_url = ? ORDER BY issued DESC",
- $this->associations_table_name);
- $params = array($server_url);
- }
-
- $assoc = $this->connection->getRow($sql, $types, $params);
-
- if (!$assoc || PEAR::isError($assoc)) {
- return null;
- } else {
- $association = new Auth_OpenID_Association($assoc['handle'],
- stream_get_contents(
- $assoc['secret']),
- $assoc['issued'],
- $assoc['lifetime'],
- $assoc['assoc_type']);
- fclose($assoc['secret']);
- return $association;
- }
- }
-
- function removeAssociation($server_url, $handle)
- {
- $r = $this->connection->execParam(
- sprintf("DELETE FROM %s WHERE server_url = ? AND handle = ?",
- $this->associations_table_name),
- array($server_url, $handle));
-
- if (PEAR::isError($r) || $r == 0) {
- return false;
- }
- return true;
- }
-
- function useNonce($server_url, $timestamp, $salt)
- {
- global $Auth_OpenID_SKEW;
-
- if (abs($timestamp - time()) > $Auth_OpenID_SKEW ) {
- return false;
- }
-
- $fields = array(
- "timestamp" => $timestamp,
- "salt" => $salt
- );
-
- if (!empty($server_url)) {
- $fields["server_url"] = $server_url;
- }
-
- $r = $this->connection->autoExecute(
- $this->nonces_table_name,
- $fields,
- MDB2_AUTOQUERY_INSERT);
-
- if (PEAR::isError($r)) {
- return false;
- }
- return true;
- }
-
- /**
- * Resets the store by removing all records from the store's
- * tables.
- */
- function reset()
- {
- $this->connection->query(sprintf("DELETE FROM %s",
- $this->associations_table_name));
-
- $this->connection->query(sprintf("DELETE FROM %s",
- $this->nonces_table_name));
- }
-
-}
-
-?>
-
--- a/lib/openid-php/Auth/OpenID/MemcachedStore.php
+++ /dev/null
@@ -1,208 +1,1 @@
-<?php
-/**
- * This file supplies a memcached store backend for OpenID servers and
- * consumers.
- *
- * PHP versions 4 and 5
- *
- * LICENSE: See the COPYING file included in this distribution.
- *
- * @package OpenID
- * @author Artemy Tregubenko <me@arty.name>
- * @copyright 2008 JanRain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- * Contributed by Open Web Technologies <http://openwebtech.ru/>
- */
-
-/**
- * Import the interface for creating a new store class.
- */
-require_once 'Auth/OpenID/Interface.php';
-
-/**
- * This is a memcached-based store for OpenID associations and
- * nonces.
- *
- * As memcache has limit of 250 chars for key length,
- * server_url, handle and salt are hashed with sha1().
- *
- * Most of the methods of this class are implementation details.
- * People wishing to just use this store need only pay attention to
- * the constructor.
- *
- * @package OpenID
- */
-class Auth_OpenID_MemcachedStore extends Auth_OpenID_OpenIDStore {
-
- /**
- * Initializes a new {@link Auth_OpenID_MemcachedStore} instance.
- * Just saves memcached object as property.
- *
- * @param resource connection Memcache connection resourse
- */
- function Auth_OpenID_MemcachedStore($connection, $compress = false)
- {
- $this->connection = $connection;
- $this->compress = $compress ? MEMCACHE_COMPRESSED : 0;
- }
-
- /**
- * Store association until its expiration time in memcached.
- * Overwrites any existing association with same server_url and
- * handle. Handles list of associations for every server.
- */
- function storeAssociation($server_url, $association)
- {
- // create memcached keys for association itself
- // and list of associations for this server
- $associationKey = $this->associationKey($server_url,
- $association->handle);
- $serverKey = $this->associationServerKey($server_url);
-
- // get list of associations
- $serverAssociations = $this->connection->get($serverKey);
-
- // if no such list, initialize it with empty array
- if (!$serverAssociations) {
- $serverAssociations = array();
- }
- // and store given association key in it
- $serverAssociations[$association->issued] = $associationKey;
-
- // save associations' keys list
- $this->connection->set(
- $serverKey,
- $serverAssociations,
- $this->compress
- );
- // save association itself
- $this->connection->set(
- $associationKey,
- $association,
- $this->compress,
- $association->issued + $association->lifetime);
- }
-
- /**
- * Read association from memcached. If no handle given
- * and multiple associations found, returns latest issued
- */
- function getAssociation($server_url, $handle = null)
- {
- // simple case: handle given
- if ($handle !== null) {
- // get association, return null if failed
- $association = $this->connection->get(
- $this->associationKey($server_url, $handle));
- return $association ? $association : null;
- }
-
- // no handle given, working with list
- // create key for list of associations
- $serverKey = $this->associationServerKey($server_url);
-
- // get list of associations
- $serverAssociations = $this->connection->get($serverKey);
- // return null if failed or got empty list
- if (!$serverAssociations) {
- return null;
- }
-
- // get key of most recently issued association
- $keys = array_keys($serverAssociations);
- sort($keys);
- $lastKey = $serverAssociations[array_pop($keys)];
-
- // get association, return null if failed
- $association = $this->connection->get($lastKey);
- return $association ? $association : null;
- }
-
- /**
- * Immediately delete association from memcache.
- */
- function removeAssociation($server_url, $handle)
- {
- // create memcached keys for association itself
- // and list of associations for this server
- $serverKey = $this->associationServerKey($server_url);
- $associationKey = $this->associationKey($server_url,
- $handle);
-
- // get list of associations
- $serverAssociations = $this->connection->get($serverKey);
- // return null if failed or got empty list
- if (!$serverAssociations) {
- return false;
- }
-
- // ensure that given association key exists in list
- $serverAssociations = array_flip($serverAssociations);
- if (!array_key_exists($associationKey, $serverAssociations)) {
- return false;
- }
-
- // remove given association key from list
- unset($serverAssociations[$associationKey]);
- $serverAssociations = array_flip($serverAssociations);
-
- // save updated list
- $this->connection->set(
- $serverKey,
- $serverAssociations,
- $this->compress
- );
-
- // delete association
- return $this->connection->delete($associationKey);
- }
-
- /**
- * Create nonce for server and salt, expiring after
- * $Auth_OpenID_SKEW seconds.
- */
- function useNonce($server_url, $timestamp, $salt)
- {
- global $Auth_OpenID_SKEW;
-
- // save one request to memcache when nonce obviously expired
- if (abs($timestamp - time()) > $Auth_OpenID_SKEW) {
- return false;
- }
-
- // returns false when nonce already exists
- // otherwise adds nonce
- return $this->connection->add(
- 'openid_nonce_' . sha1($server_url) . '_' . sha1($salt),
- 1, // any value here
- $this->compress,
- $Auth_OpenID_SKEW);
- }
-
- /**
- * Memcache key is prefixed with 'openid_association_' string.
- */
- function associationKey($server_url, $handle = null)
- {
- return 'openid_association_' . sha1($server_url) . '_' . sha1($handle);
- }
-
- /**
- * Memcache key is prefixed with 'openid_association_' string.
- */
- function associationServerKey($server_url)
- {
- return 'openid_association_server_' . sha1($server_url);
- }
-
- /**
- * Report that this storage doesn't support cleanup
- */
- function supportsCleanup()
- {
- return false;
- }
-}
-
-
--- a/lib/openid-php/Auth/OpenID/Message.php
+++ /dev/null
@@ -1,921 +1,1 @@
-<?php
-/**
- * Extension argument processing code
- *
- * @package OpenID
- */
-
-/**
- * Import tools needed to deal with messages.
- */
-require_once 'Auth/OpenID.php';
-require_once 'Auth/OpenID/KVForm.php';
-require_once 'Auth/Yadis/XML.php';
-require_once 'Auth/OpenID/Consumer.php'; // For Auth_OpenID_FailureResponse
-
-// This doesn't REALLY belong here, but where is better?
-define('Auth_OpenID_IDENTIFIER_SELECT',
- "http://specs.openid.net/auth/2.0/identifier_select");
-
-// URI for Simple Registration extension, the only commonly deployed
-// OpenID 1.x extension, and so a special case
-define('Auth_OpenID_SREG_URI', 'http://openid.net/sreg/1.0');
-
-// The OpenID 1.X namespace URI
-define('Auth_OpenID_OPENID1_NS', 'http://openid.net/signon/1.0');
-define('Auth_OpenID_THE_OTHER_OPENID1_NS', 'http://openid.net/signon/1.1');
-
-function Auth_OpenID_isOpenID1($ns)
-{
- return ($ns == Auth_OpenID_THE_OTHER_OPENID1_NS) ||
- ($ns == Auth_OpenID_OPENID1_NS);
-}
-
-// The OpenID 2.0 namespace URI
-define('Auth_OpenID_OPENID2_NS', 'http://specs.openid.net/auth/2.0');
-
-// The namespace consisting of pairs with keys that are prefixed with
-// "openid." but not in another namespace.
-define('Auth_OpenID_NULL_NAMESPACE', 'Null namespace');
-
-// The null namespace, when it is an allowed OpenID namespace
-define('Auth_OpenID_OPENID_NS', 'OpenID namespace');
-
-// The top-level namespace, excluding all pairs with keys that start
-// with "openid."
-define('Auth_OpenID_BARE_NS', 'Bare namespace');
-
-// Sentinel for Message implementation to indicate that getArg should
-// return null instead of returning a default.
-define('Auth_OpenID_NO_DEFAULT', 'NO DEFAULT ALLOWED');
-
-// Limit, in bytes, of identity provider and return_to URLs, including
-// response payload. See OpenID 1.1 specification, Appendix D.
-define('Auth_OpenID_OPENID1_URL_LIMIT', 2047);
-
-// All OpenID protocol fields. Used to check namespace aliases.
-global $Auth_OpenID_OPENID_PROTOCOL_FIELDS;
-$Auth_OpenID_OPENID_PROTOCOL_FIELDS = array(
- 'ns', 'mode', 'error', 'return_to', 'contact', 'reference',
- 'signed', 'assoc_type', 'session_type', 'dh_modulus', 'dh_gen',
- 'dh_consumer_public', 'claimed_id', 'identity', 'realm',
- 'invalidate_handle', 'op_endpoint', 'response_nonce', 'sig',
- 'assoc_handle', 'trust_root', 'openid');
-
-// Global namespace / alias registration map. See
-// Auth_OpenID_registerNamespaceAlias.
-global $Auth_OpenID_registered_aliases;
-$Auth_OpenID_registered_aliases = array();
-
-/**
- * Registers a (namespace URI, alias) mapping in a global namespace
- * alias map. Raises NamespaceAliasRegistrationError if either the
- * namespace URI or alias has already been registered with a different
- * value. This function is required if you want to use a namespace
- * with an OpenID 1 message.
- */
-function Auth_OpenID_registerNamespaceAlias($namespace_uri, $alias)
-{
- global $Auth_OpenID_registered_aliases;
-
- if (Auth_OpenID::arrayGet($Auth_OpenID_registered_aliases,
- $alias) == $namespace_uri) {
- return true;
- }
-
- if (in_array($namespace_uri,
- array_values($Auth_OpenID_registered_aliases))) {
- return false;
- }
-
- if (in_array($alias, array_keys($Auth_OpenID_registered_aliases))) {
- return false;
- }
-
- $Auth_OpenID_registered_aliases[$alias] = $namespace_uri;
- return true;
-}
-
-/**
- * Removes a (namespace_uri, alias) registration from the global
- * namespace alias map. Returns true if the removal succeeded; false
- * if not (if the mapping did not exist).
- */
-function Auth_OpenID_removeNamespaceAlias($namespace_uri, $alias)
-{
- global $Auth_OpenID_registered_aliases;
-
- if (Auth_OpenID::arrayGet($Auth_OpenID_registered_aliases,
- $alias) === $namespace_uri) {
- unset($Auth_OpenID_registered_aliases[$alias]);
- return true;
- }
-
- return false;
-}
-
-/**
- * An Auth_OpenID_Mapping maintains a mapping from arbitrary keys to
- * arbitrary values. (This is unlike an ordinary PHP array, whose
- * keys may be only simple scalars.)
- *
- * @package OpenID
- */
-class Auth_OpenID_Mapping {
- /**
- * Initialize a mapping. If $classic_array is specified, its keys
- * and values are used to populate the mapping.
- */
- function Auth_OpenID_Mapping($classic_array = null)
- {
- $this->keys = array();
- $this->values = array();
-
- if (is_array($classic_array)) {
- foreach ($classic_array as $key => $value) {
- $this->set($key, $value);
- }
- }
- }
-
- /**
- * Returns true if $thing is an Auth_OpenID_Mapping object; false
- * if not.
- */
- static function isA($thing)
- {
- return (is_object($thing) &&
- strtolower(get_class($thing)) == 'auth_openid_mapping');
- }
-
- /**
- * Returns an array of the keys in the mapping.
- */
- function keys()
- {
- return $this->keys;
- }
-
- /**
- * Returns an array of values in the mapping.
- */
- function values()
- {
- return $this->values;
- }
-
- /**
- * Returns an array of (key, value) pairs in the mapping.
- */
- function items()
- {
- $temp = array();
-
- for ($i = 0; $i < count($this->keys); $i++) {
- $temp[] = array($this->keys[$i],
- $this->values[$i]);
- }
- return $temp;
- }
-
- /**
- * Returns the "length" of the mapping, or the number of keys.
- */
- function len()
- {
- return count($this->keys);
- }
-
- /**
- * Sets a key-value pair in the mapping. If the key already
- * exists, its value is replaced with the new value.
- */
- function set($key, $value)
- {
- $index = array_search($key, $this->keys);
-
- if ($index !== false) {
- $this->values[$index] = $value;
- } else {
- $this->keys[] = $key;
- $this->values[] = $value;
- }
- }
-
- /**
- * Gets a specified value from the mapping, associated with the
- * specified key. If the key does not exist in the mapping,
- * $default is returned instead.
- */
- function get($key, $default = null)
- {
- $index = array_search($key, $this->keys);
-
- if ($index !== false) {
- return $this->values[$index];
- } else {
- return $default;
- }
- }
-
- /**
- * @access private
- */
- function _reflow()
- {
- // PHP is broken yet again. Sort the arrays to remove the
- // hole in the numeric indexes that make up the array.
- $old_keys = $this->keys;
- $old_values = $this->values;
-
- $this->keys = array();
- $this->values = array();
-
- foreach ($old_keys as $k) {
- $this->keys[] = $k;
- }
-
- foreach ($old_values as $v) {
- $this->values[] = $v;
- }
- }
-
- /**
- * Deletes a key-value pair from the mapping with the specified
- * key.
- */
- function del($key)
- {
- $index = array_search($key, $this->keys);
-
- if ($index !== false) {
- unset($this->keys[$index]);
- unset($this->values[$index]);
- $this->_reflow();
- return true;
- }
- return false;
- }
-
- /**
- * Returns true if the specified value has a key in the mapping;
- * false if not.
- */
- function contains($value)
- {
- return (array_search($value, $this->keys) !== false);
- }
-}
-
-/**
- * Maintains a bijective map between namespace uris and aliases.
- *
- * @package OpenID
- */
-class Auth_OpenID_NamespaceMap {
- function Auth_OpenID_NamespaceMap()
- {
- $this->alias_to_namespace = new Auth_OpenID_Mapping();
- $this->namespace_to_alias = new Auth_OpenID_Mapping();
- $this->implicit_namespaces = array();
- }
-
- function getAlias($namespace_uri)
- {
- return $this->namespace_to_alias->get($namespace_uri);
- }
-
- function getNamespaceURI($alias)
- {
- return $this->alias_to_namespace->get($alias);
- }
-
- function iterNamespaceURIs()
- {
- // Return an iterator over the namespace URIs
- return $this->namespace_to_alias->keys();
- }
-
- function iterAliases()
- {
- // Return an iterator over the aliases"""
- return $this->alias_to_namespace->keys();
- }
-
- function iteritems()
- {
- return $this->namespace_to_alias->items();
- }
-
- function isImplicit($namespace_uri)
- {
- return in_array($namespace_uri, $this->implicit_namespaces);
- }
-
- function addAlias($namespace_uri, $desired_alias, $implicit=false)
- {
- // Add an alias from this namespace URI to the desired alias
- global $Auth_OpenID_OPENID_PROTOCOL_FIELDS;
-
- // Check that desired_alias is not an openid protocol field as
- // per the spec.
- if (in_array($desired_alias, $Auth_OpenID_OPENID_PROTOCOL_FIELDS)) {
- Auth_OpenID::log("\"%s\" is not an allowed namespace alias",
- $desired_alias);
- return null;
- }
-
- // Check that desired_alias does not contain a period as per
- // the spec.
- if (strpos($desired_alias, '.') !== false) {
- Auth_OpenID::log('"%s" must not contain a dot', $desired_alias);
- return null;
- }
-
- // Check that there is not a namespace already defined for the
- // desired alias
- $current_namespace_uri =
- $this->alias_to_namespace->get($desired_alias);
-
- if (($current_namespace_uri !== null) &&
- ($current_namespace_uri != $namespace_uri)) {
- Auth_OpenID::log('Cannot map "%s" because previous mapping exists',
- $namespace_uri);
- return null;
- }
-
- // Check that there is not already a (different) alias for
- // this namespace URI
- $alias = $this->namespace_to_alias->get($namespace_uri);
-
- if (($alias !== null) && ($alias != $desired_alias)) {
- Auth_OpenID::log('Cannot map %s to alias %s. ' .
- 'It is already mapped to alias %s',
- $namespace_uri, $desired_alias, $alias);
- return null;
- }
-
- assert((Auth_OpenID_NULL_NAMESPACE === $desired_alias) ||
- is_string($desired_alias));
-
- $this->alias_to_namespace->set($desired_alias, $namespace_uri);
- $this->namespace_to_alias->set($namespace_uri, $desired_alias);
- if ($implicit) {
- array_push($this->implicit_namespaces, $namespace_uri);
- }
-
- return $desired_alias;
- }
-
- function add($namespace_uri)
- {
- // Add this namespace URI to the mapping, without caring what
- // alias it ends up with
-
- // See if this namespace is already mapped to an alias
- $alias = $this->namespace_to_alias->get($namespace_uri);
-
- if ($alias !== null) {
- return $alias;
- }
-
- // Fall back to generating a numerical alias
- $i = 0;
- while (1) {
- $alias = 'ext' . strval($i);
- if ($this->addAlias($namespace_uri, $alias) === null) {
- $i += 1;
- } else {
- return $alias;
- }
- }
-
- // Should NEVER be reached!
- return null;
- }
-
- function contains($namespace_uri)
- {
- return $this->isDefined($namespace_uri);
- }
-
- function isDefined($namespace_uri)
- {
- return $this->namespace_to_alias->contains($namespace_uri);
- }
-}
-
-/**
- * In the implementation of this object, null represents the global
- * namespace as well as a namespace with no key.
- *
- * @package OpenID
- */
-class Auth_OpenID_Message {
-
- function Auth_OpenID_Message($openid_namespace = null)
- {
- // Create an empty Message
- $this->allowed_openid_namespaces = array(
- Auth_OpenID_OPENID1_NS,
- Auth_OpenID_THE_OTHER_OPENID1_NS,
- Auth_OpenID_OPENID2_NS);
-
- $this->args = new Auth_OpenID_Mapping();
- $this->namespaces = new Auth_OpenID_NamespaceMap();
- if ($openid_namespace === null) {
- $this->_openid_ns_uri = null;
- } else {
- $implicit = Auth_OpenID_isOpenID1($openid_namespace);
- $this->setOpenIDNamespace($openid_namespace, $implicit);
- }
- }
-
- function isOpenID1()
- {
- return Auth_OpenID_isOpenID1($this->getOpenIDNamespace());
- }
-
- function isOpenID2()
- {
- return $this->getOpenIDNamespace() == Auth_OpenID_OPENID2_NS;
- }
-
- static function fromPostArgs($args)
- {
- // Construct a Message containing a set of POST arguments
- $obj = new Auth_OpenID_Message();
-
- // Partition into "openid." args and bare args
- $openid_args = array();
- foreach ($args as $key => $value) {
-
- if (is_array($value)) {
- return null;
- }
-
- $parts = explode('.', $key, 2);
-
- if (count($parts) == 2) {
- list($prefix, $rest) = $parts;
- } else {
- $prefix = null;
- }
-
- if ($prefix != 'openid') {
- $obj->args->set(array(Auth_OpenID_BARE_NS, $key), $value);
- } else {
- $openid_args[$rest] = $value;
- }
- }
-
- if ($obj->_fromOpenIDArgs($openid_args)) {
- return $obj;
- } else {
- return null;
- }
- }
-
- static function fromOpenIDArgs($openid_args)
- {
- // Takes an array.
-
- // Construct a Message from a parsed KVForm message
- $obj = new Auth_OpenID_Message();
- if ($obj->_fromOpenIDArgs($openid_args)) {
- return $obj;
- } else {
- return null;
- }
- }
-
- /**
- * @access private
- */
- function _fromOpenIDArgs($openid_args)
- {
- global $Auth_OpenID_registered_aliases;
-
- // Takes an Auth_OpenID_Mapping instance OR an array.
-
- if (!Auth_OpenID_Mapping::isA($openid_args)) {
- $openid_args = new Auth_OpenID_Mapping($openid_args);
- }
-
- $ns_args = array();
-
- // Resolve namespaces
- foreach ($openid_args->items() as $pair) {
- list($rest, $value) = $pair;
-
- $parts = explode('.', $rest, 2);
-
- if (count($parts) == 2) {
- list($ns_alias, $ns_key) = $parts;
- } else {
- $ns_alias = Auth_OpenID_NULL_NAMESPACE;
- $ns_key = $rest;
- }
-
- if ($ns_alias == 'ns') {
- if ($this->namespaces->addAlias($value, $ns_key) === null) {
- return false;
- }
- } else if (($ns_alias == Auth_OpenID_NULL_NAMESPACE) &&
- ($ns_key == 'ns')) {
- // null namespace
- if ($this->setOpenIDNamespace($value, false) === false) {
- return false;
- }
- } else {
- $ns_args[] = array($ns_alias, $ns_key, $value);
- }
- }
-
- if (!$this->getOpenIDNamespace()) {
- if ($this->setOpenIDNamespace(Auth_OpenID_OPENID1_NS, true) ===
- false) {
- return false;
- }
- }
-
- // Actually put the pairs into the appropriate namespaces
- foreach ($ns_args as $triple) {
- list($ns_alias, $ns_key, $value) = $triple;
- $ns_uri = $this->namespaces->getNamespaceURI($ns_alias);
- if ($ns_uri === null) {
- $ns_uri = $this->_getDefaultNamespace($ns_alias);
- if ($ns_uri === null) {
-
- $ns_uri = Auth_OpenID_OPENID_NS;
- $ns_key = sprintf('%s.%s', $ns_alias, $ns_key);
- } else {
- $this->namespaces->addAlias($ns_uri, $ns_alias, true);
- }
- }
-
- $this->setArg($ns_uri, $ns_key, $value);
- }
-
- return true;
- }
-
- function _getDefaultNamespace($mystery_alias)
- {
- global $Auth_OpenID_registered_aliases;
- if ($this->isOpenID1()) {
- return @$Auth_OpenID_registered_aliases[$mystery_alias];
- }
- return null;
- }
-
- function setOpenIDNamespace($openid_ns_uri, $implicit)
- {
- if (!in_array($openid_ns_uri, $this->allowed_openid_namespaces)) {
- Auth_OpenID::log('Invalid null namespace: "%s"', $openid_ns_uri);
- return false;
- }
-
- $succeeded = $this->namespaces->addAlias($openid_ns_uri,
- Auth_OpenID_NULL_NAMESPACE,
- $implicit);
- if ($succeeded === false) {
- return false;
- }
-
- $this->_openid_ns_uri = $openid_ns_uri;
-
- return true;
- }
-
- function getOpenIDNamespace()
- {
- return $this->_openid_ns_uri;
- }
-
- static function fromKVForm($kvform_string)
- {
- // Create a Message from a KVForm string
- return Auth_OpenID_Message::fromOpenIDArgs(
- Auth_OpenID_KVForm::toArray($kvform_string));
- }
-
- function copy()
- {
- return $this;
- }
-
- function toPostArgs()
- {
- // Return all arguments with openid. in front of namespaced
- // arguments.
-
- $args = array();
-
- // Add namespace definitions to the output
- foreach ($this->namespaces->iteritems() as $pair) {
- list($ns_uri, $alias) = $pair;
- if ($this->namespaces->isImplicit($ns_uri)) {
- continue;
- }
- if ($alias == Auth_OpenID_NULL_NAMESPACE) {
- $ns_key = 'openid.ns';
- } else {
- $ns_key = 'openid.ns.' . $alias;
- }
- $args[$ns_key] = $ns_uri;
- }
-
- foreach ($this->args->items() as $pair) {
- list($ns_parts, $value) = $pair;
- list($ns_uri, $ns_key) = $ns_parts;
- $key = $this->getKey($ns_uri, $ns_key);
- $args[$key] = $value;
- }
-
- return $args;
- }
-
- function toArgs()
- {
- // Return all namespaced arguments, failing if any
- // non-namespaced arguments exist.
- $post_args = $this->toPostArgs();
- $kvargs = array();
- foreach ($post_args as $k => $v) {
- if (strpos($k, 'openid.') !== 0) {
- // raise ValueError(
- // 'This message can only be encoded as a POST, because it '
- // 'contains arguments that are not prefixed with "openid."')
- return null;
- } else {
- $kvargs[substr($k, 7)] = $v;
- }
- }
-
- return $kvargs;
- }
-
- function toFormMarkup($action_url, $form_tag_attrs = null,
- $submit_text = "Continue")
- {
- $form = "<form accept-charset=\"UTF-8\" ".
- "enctype=\"application/x-www-form-urlencoded\"";
-
- if (!$form_tag_attrs) {
- $form_tag_attrs = array();
- }
-
- $form_tag_attrs['action'] = $action_url;
- $form_tag_attrs['method'] = 'post';
-
- unset($form_tag_attrs['enctype']);
- unset($form_tag_attrs['accept-charset']);
-
- if ($form_tag_attrs) {
- foreach ($form_tag_attrs as $name => $attr) {
- $form .= sprintf(" %s=\"%s\"", $name, $attr);
- }
- }
-
- $form .= ">\n";
-
- foreach ($this->toPostArgs() as $name => $value) {
- $form .= sprintf(
- "<input type=\"hidden\" name=\"%s\" value=\"%s\" />\n",
- $name, urldecode($value));
- }
-
- $form .= sprintf("<input type=\"submit\" value=\"%s\" />\n",
- $submit_text);
-
- $form .= "</form>\n";
-
- return $form;
- }
-
- function toURL($base_url)
- {
- // Generate a GET URL with the parameters in this message
- // attached as query parameters.
- return Auth_OpenID::appendArgs($base_url, $this->toPostArgs());
- }
-
- function toKVForm()
- {
- // Generate a KVForm string that contains the parameters in
- // this message. This will fail if the message contains
- // arguments outside of the 'openid.' prefix.
- return Auth_OpenID_KVForm::fromArray($this->toArgs());
- }
-
- function toURLEncoded()
- {
- // Generate an x-www-urlencoded string
- $args = array();
-
- foreach ($this->toPostArgs() as $k => $v) {
- $args[] = array($k, $v);
- }
-
- sort($args);
- return Auth_OpenID::httpBuildQuery($args);
- }
-
- /**
- * @access private
- */
- function _fixNS($namespace)
- {
- // Convert an input value into the internally used values of
- // this object
-
- if ($namespace == Auth_OpenID_OPENID_NS) {
- if ($this->_openid_ns_uri === null) {
- return new Auth_OpenID_FailureResponse(null,
- 'OpenID namespace not set');
- } else {
- $namespace = $this->_openid_ns_uri;
- }
- }
-
- if (($namespace != Auth_OpenID_BARE_NS) &&
- (!is_string($namespace))) {
- //TypeError
- $err_msg = sprintf("Namespace must be Auth_OpenID_BARE_NS, ".
- "Auth_OpenID_OPENID_NS or a string. got %s",
- print_r($namespace, true));
- return new Auth_OpenID_FailureResponse(null, $err_msg);
- }
-
- if (($namespace != Auth_OpenID_BARE_NS) &&
- (strpos($namespace, ':') === false)) {
- // fmt = 'OpenID 2.0 namespace identifiers SHOULD be URIs. Got %r'
- // warnings.warn(fmt % (namespace,), DeprecationWarning)
-
- if ($namespace == 'sreg') {
- // fmt = 'Using %r instead of "sreg" as namespace'
- // warnings.warn(fmt % (SREG_URI,), DeprecationWarning,)
- return Auth_OpenID_SREG_URI;
- }
- }
-
- return $namespace;
- }
-
- function hasKey($namespace, $ns_key)
- {
- $namespace = $this->_fixNS($namespace);
- if (Auth_OpenID::isFailure($namespace)) {
- // XXX log me
- return false;
- } else {
- return $this->args->contains(array($namespace, $ns_key));
- }
- }
-
- function getKey($namespace, $ns_key)
- {
- // Get the key for a particular namespaced argument
- $namespace = $this->_fixNS($namespace);
- if (Auth_OpenID::isFailure($namespace)) {
- return $namespace;
- }
- if ($namespace == Auth_OpenID_BARE_NS) {
- return $ns_key;
- }
-
- $ns_alias = $this->namespaces->getAlias($namespace);
-
- // No alias is defined, so no key can exist
- if ($ns_alias === null) {
- return null;
- }
-
- if ($ns_alias == Auth_OpenID_NULL_NAMESPACE) {
- $tail = $ns_key;
- } else {
- $tail = sprintf('%s.%s', $ns_alias, $ns_key);
- }
-
- return 'openid.' . $tail;
- }
-
- function getArg($namespace, $key, $default = null)
- {
- // Get a value for a namespaced key.
- $namespace = $this->_fixNS($namespace);
-
- if (Auth_OpenID::isFailure($namespace)) {
- return $namespace;
- } else {
- if ((!$this->args->contains(array($namespace, $key))) &&
- ($default == Auth_OpenID_NO_DEFAULT)) {
- $err_msg = sprintf("Namespace %s missing required field %s",
- $namespace, $key);
- return new Auth_OpenID_FailureResponse(null, $err_msg);
- } else {
- return $this->args->get(array($namespace, $key), $default);
- }
- }
- }
-
- function getArgs($namespace)
- {
- // Get the arguments that are defined for this namespace URI
-
- $namespace = $this->_fixNS($namespace);
- if (Auth_OpenID::isFailure($namespace)) {
- return $namespace;
- } else {
- $stuff = array();
- foreach ($this->args->items() as $pair) {
- list($key, $value) = $pair;
- list($pair_ns, $ns_key) = $key;
- if ($pair_ns == $namespace) {
- $stuff[$ns_key] = $value;
- }
- }
-
- return $stuff;
- }
- }
-
- function updateArgs($namespace, $updates)
- {
- // Set multiple key/value pairs in one call
-
- $namespace = $this->_fixNS($namespace);
-
- if (Auth_OpenID::isFailure($namespace)) {
- return $namespace;
- } else {
- foreach ($updates as $k => $v) {
- $this->setArg($namespace, $k, $v);
- }
- return true;
- }
- }
-
- function setArg($namespace, $key, $value)
- {
- // Set a single argument in this namespace
- $namespace = $this->_fixNS($namespace);
-
- if (Auth_OpenID::isFailure($namespace)) {
- return $namespace;
- } else {
- $this->args->set(array($namespace, $key), $value);
- if ($namespace !== Auth_OpenID_BARE_NS) {
- $this->namespaces->add($namespace);
- }
- return true;
- }
- }
-
- function delArg($namespace, $key)
- {
- $namespace = $this->_fixNS($namespace);
-
- if (Auth_OpenID::isFailure($namespace)) {
- return $namespace;
- } else {
- return $this->args->del(array($namespace, $key));
- }
- }
-
- function getAliasedArg($aliased_key, $default = null)
- {
- if ($aliased_key == 'ns') {
- // Return the namespace URI for the OpenID namespace
- return $this->getOpenIDNamespace();
- }
-
- $parts = explode('.', $aliased_key, 2);
-
- if (count($parts) != 2) {
- $ns = null;
- } else {
- list($alias, $key) = $parts;
-
- if ($alias == 'ns') {
- // Return the namespace URI for a namespace alias
- // parameter.
- return $this->namespaces->getNamespaceURI($key);
- } else {
- $ns = $this->namespaces->getNamespaceURI($alias);
- }
- }
-
- if ($ns === null) {
- $key = $aliased_key;
- $ns = $this->getOpenIDNamespace();
- }
-
- return $this->getArg($ns, $key, $default);
- }
-}
-
-
-
--- a/lib/openid-php/Auth/OpenID/MySQLStore.php
+++ /dev/null
@@ -1,78 +1,1 @@
-<?php
-/**
- * A MySQL store.
- *
- * @package OpenID
- */
-
-/**
- * Require the base class file.
- */
-require_once "Auth/OpenID/SQLStore.php";
-
-/**
- * An SQL store that uses MySQL as its backend.
- *
- * @package OpenID
- */
-class Auth_OpenID_MySQLStore extends Auth_OpenID_SQLStore {
- /**
- * @access private
- */
- function setSQL()
- {
- $this->sql['nonce_table'] =
- "CREATE TABLE %s (\n".
- " server_url VARCHAR(2047) NOT NULL,\n".
- " timestamp INTEGER NOT NULL,\n".
- " salt CHAR(40) NOT NULL,\n".
- " UNIQUE (server_url(255), timestamp, salt)\n".
- ") ENGINE=InnoDB";
-
- $this->sql['assoc_table'] =
- "CREATE TABLE %s (\n".
- " server_url BLOB NOT NULL,\n".
- " handle VARCHAR(255) NOT NULL,\n".
- " secret BLOB NOT NULL,\n".
- " issued INTEGER NOT NULL,\n".
- " lifetime INTEGER NOT NULL,\n".
- " assoc_type VARCHAR(64) NOT NULL,\n".
- " PRIMARY KEY (server_url(255), handle)\n".
- ") ENGINE=InnoDB";
-
- $this->sql['set_assoc'] =
- "REPLACE INTO %s (server_url, handle, secret, issued,\n".
- " lifetime, assoc_type) VALUES (?, ?, !, ?, ?, ?)";
-
- $this->sql['get_assocs'] =
- "SELECT handle, secret, issued, lifetime, assoc_type FROM %s ".
- "WHERE server_url = ?";
-
- $this->sql['get_assoc'] =
- "SELECT handle, secret, issued, lifetime, assoc_type FROM %s ".
- "WHERE server_url = ? AND handle = ?";
-
- $this->sql['remove_assoc'] =
- "DELETE FROM %s WHERE server_url = ? AND handle = ?";
-
- $this->sql['add_nonce'] =
- "INSERT INTO %s (server_url, timestamp, salt) VALUES (?, ?, ?)";
-
- $this->sql['clean_nonce'] =
- "DELETE FROM %s WHERE timestamp < ?";
-
- $this->sql['clean_assoc'] =
- "DELETE FROM %s WHERE issued + lifetime < ?";
- }
-
- /**
- * @access private
- */
- function blobEncode($blob)
- {
- return "0x" . bin2hex($blob);
- }
-}
-
-
--- a/lib/openid-php/Auth/OpenID/Nonce.php
+++ /dev/null
@@ -1,109 +1,1 @@
-<?php
-/**
- * Nonce-related functionality.
- *
- * @package OpenID
- */
-
-/**
- * Need CryptUtil to generate random strings.
- */
-require_once 'Auth/OpenID/CryptUtil.php';
-
-/**
- * This is the characters that the nonces are made from.
- */
-define('Auth_OpenID_Nonce_CHRS',"abcdefghijklmnopqrstuvwxyz" .
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
-
-// Keep nonces for five hours (allow five hours for the combination of
-// request time and clock skew). This is probably way more than is
-// necessary, but there is not much overhead in storing nonces.
-global $Auth_OpenID_SKEW;
-$Auth_OpenID_SKEW = 60 * 60 * 5;
-
-define('Auth_OpenID_Nonce_REGEX',
- '/(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z(.*)/');
-
-define('Auth_OpenID_Nonce_TIME_FMT',
- '%Y-%m-%dT%H:%M:%SZ');
-
-function Auth_OpenID_splitNonce($nonce_string)
-{
- // Extract a timestamp from the given nonce string
- $result = preg_match(Auth_OpenID_Nonce_REGEX, $nonce_string, $matches);
- if ($result != 1 || count($matches) != 8) {
- return null;
- }
-
- list($unused,
- $tm_year,
- $tm_mon,
- $tm_mday,
- $tm_hour,
- $tm_min,
- $tm_sec,
- $uniquifier) = $matches;
-
- $timestamp =
- @gmmktime($tm_hour, $tm_min, $tm_sec, $tm_mon, $tm_mday, $tm_year);
-
- if ($timestamp === false || $timestamp < 0) {
- return null;
- }
-
- return array($timestamp, $uniquifier);
-}
-
-function Auth_OpenID_checkTimestamp($nonce_string,
- $allowed_skew = null,
- $now = null)
-{
- // Is the timestamp that is part of the specified nonce string
- // within the allowed clock-skew of the current time?
- global $Auth_OpenID_SKEW;
-
- if ($allowed_skew === null) {
- $allowed_skew = $Auth_OpenID_SKEW;
- }
-
- $parts = Auth_OpenID_splitNonce($nonce_string);
- if ($parts == null) {
- return false;
- }
-
- if ($now === null) {
- $now = time();
- }
-
- $stamp = $parts[0];
-
- // Time after which we should not use the nonce
- $past = $now - $allowed_skew;
-
- // Time that is too far in the future for us to allow
- $future = $now + $allowed_skew;
-
- // the stamp is not too far in the future and is not too far
- // in the past
- return (($past <= $stamp) && ($stamp <= $future));
-}
-
-function Auth_OpenID_mkNonce($when = null)
-{
- // Generate a nonce with the current timestamp
- $salt = Auth_OpenID_CryptUtil::randomString(
- 6, Auth_OpenID_Nonce_CHRS);
- if ($when === null) {
- // It's safe to call time() with no arguments; it returns a
- // GMT unix timestamp on PHP 4 and PHP 5. gmmktime() with no
- // args returns a local unix timestamp on PHP 4, so don't use
- // that.
- $when = time();
- }
- $time_str = gmstrftime(Auth_OpenID_Nonce_TIME_FMT, $when);
- return $time_str . $salt;
-}
-
-
--- a/lib/openid-php/Auth/OpenID/PAPE.php
+++ /dev/null
@@ -1,301 +1,1 @@
-<?php
-/**
- * An implementation of the OpenID Provider Authentication Policy
- * Extension 1.0
- *
- * See:
- * http://openid.net/developers/specs/
- */
-
-require_once "Auth/OpenID/Extension.php";
-
-define('Auth_OpenID_PAPE_NS_URI',
- "http://specs.openid.net/extensions/pape/1.0");
-
-define('PAPE_AUTH_MULTI_FACTOR_PHYSICAL',
- 'http://schemas.openid.net/pape/policies/2007/06/multi-factor-physical');
-define('PAPE_AUTH_MULTI_FACTOR',
- 'http://schemas.openid.net/pape/policies/2007/06/multi-factor');
-define('PAPE_AUTH_PHISHING_RESISTANT',
- 'http://schemas.openid.net/pape/policies/2007/06/phishing-resistant');
-
-define('PAPE_TIME_VALIDATOR',
- '/^[0-9]{4,4}-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]Z$/');
-/**
- * A Provider Authentication Policy request, sent from a relying party
- * to a provider
- *
- * preferred_auth_policies: The authentication policies that
- * the relying party prefers
- *
- * max_auth_age: The maximum time, in seconds, that the relying party
- * wants to allow to have elapsed before the user must re-authenticate
- */
-class Auth_OpenID_PAPE_Request extends Auth_OpenID_Extension {
-
- var $ns_alias = 'pape';
- var $ns_uri = Auth_OpenID_PAPE_NS_URI;
-
- function Auth_OpenID_PAPE_Request($preferred_auth_policies=null,
- $max_auth_age=null)
- {
- if ($preferred_auth_policies === null) {
- $preferred_auth_policies = array();
- }
-
- $this->preferred_auth_policies = $preferred_auth_policies;
- $this->max_auth_age = $max_auth_age;
- }
-
- /**
- * Add an acceptable authentication policy URI to this request
- *
- * This method is intended to be used by the relying party to add
- * acceptable authentication types to the request.
- *
- * policy_uri: The identifier for the preferred type of
- * authentication.
- */
- function addPolicyURI($policy_uri)
- {
- if (!in_array($policy_uri, $this->preferred_auth_policies)) {
- $this->preferred_auth_policies[] = $policy_uri;
- }
- }
-
- function getExtensionArgs()
- {
- $ns_args = array(
- 'preferred_auth_policies' =>
- implode(' ', $this->preferred_auth_policies)
- );
-
- if ($this->max_auth_age !== null) {
- $ns_args['max_auth_age'] = strval($this->max_auth_age);
- }
-
- return $ns_args;
- }
-
- /**
- * Instantiate a Request object from the arguments in a checkid_*
- * OpenID message
- */
- static function fromOpenIDRequest($request)
- {
- $obj = new Auth_OpenID_PAPE_Request();
- $args = $request->message->getArgs(Auth_OpenID_PAPE_NS_URI);
-
- if ($args === null || $args === array()) {
- return null;
- }
-
- $obj->parseExtensionArgs($args);
- return $obj;
- }
-
- /**
- * Set the state of this request to be that expressed in these
- * PAPE arguments
- *
- * @param args: The PAPE arguments without a namespace
- */
- function parseExtensionArgs($args)
- {
- // preferred_auth_policies is a space-separated list of policy
- // URIs
- $this->preferred_auth_policies = array();
-
- $policies_str = Auth_OpenID::arrayGet($args, 'preferred_auth_policies');
- if ($policies_str) {
- foreach (explode(' ', $policies_str) as $uri) {
- if (!in_array($uri, $this->preferred_auth_policies)) {
- $this->preferred_auth_policies[] = $uri;
- }
- }
- }
-
- // max_auth_age is base-10 integer number of seconds
- $max_auth_age_str = Auth_OpenID::arrayGet($args, 'max_auth_age');
- if ($max_auth_age_str) {
- $this->max_auth_age = Auth_OpenID::intval($max_auth_age_str);
- } else {
- $this->max_auth_age = null;
- }
- }
-
- /**
- * Given a list of authentication policy URIs that a provider
- * supports, this method returns the subsequence of those types
- * that are preferred by the relying party.
- *
- * @param supported_types: A sequence of authentication policy
- * type URIs that are supported by a provider
- *
- * @return array The sub-sequence of the supported types that are
- * preferred by the relying party. This list will be ordered in
- * the order that the types appear in the supported_types
- * sequence, and may be empty if the provider does not prefer any
- * of the supported authentication types.
- */
- function preferredTypes($supported_types)
- {
- $result = array();
-
- foreach ($supported_types as $st) {
- if (in_array($st, $this->preferred_auth_policies)) {
- $result[] = $st;
- }
- }
- return $result;
- }
-}
-
-/**
- * A Provider Authentication Policy response, sent from a provider to
- * a relying party
- */
-class Auth_OpenID_PAPE_Response extends Auth_OpenID_Extension {
-
- var $ns_alias = 'pape';
- var $ns_uri = Auth_OpenID_PAPE_NS_URI;
-
- function Auth_OpenID_PAPE_Response($auth_policies=null, $auth_time=null,
- $nist_auth_level=null)
- {
- if ($auth_policies) {
- $this->auth_policies = $auth_policies;
- } else {
- $this->auth_policies = array();
- }
-
- $this->auth_time = $auth_time;
- $this->nist_auth_level = $nist_auth_level;
- }
-
- /**
- * Add a authentication policy to this response
- *
- * This method is intended to be used by the provider to add a
- * policy that the provider conformed to when authenticating the
- * user.
- *
- * @param policy_uri: The identifier for the preferred type of
- * authentication.
- */
- function addPolicyURI($policy_uri)
- {
- if (!in_array($policy_uri, $this->auth_policies)) {
- $this->auth_policies[] = $policy_uri;
- }
- }
-
- /**
- * Create an Auth_OpenID_PAPE_Response object from a successful
- * OpenID library response.
- *
- * @param success_response $success_response A SuccessResponse
- * from Auth_OpenID_Consumer::complete()
- *
- * @returns: A provider authentication policy response from the
- * data that was supplied with the id_res response.
- */
- static function fromSuccessResponse($success_response)
- {
- $obj = new Auth_OpenID_PAPE_Response();
-
- // PAPE requires that the args be signed.
- $args = $success_response->getSignedNS(Auth_OpenID_PAPE_NS_URI);
-
- if ($args === null || $args === array()) {
- return null;
- }
-
- $result = $obj->parseExtensionArgs($args);
-
- if ($result === false) {
- return null;
- } else {
- return $obj;
- }
- }
-
- /**
- * Parse the provider authentication policy arguments into the
- * internal state of this object
- *
- * @param args: unqualified provider authentication policy
- * arguments
- *
- * @param strict: Whether to return false when bad data is
- * encountered
- *
- * @return null The data is parsed into the internal fields of
- * this object.
- */
- function parseExtensionArgs($args, $strict=false)
- {
- $policies_str = Auth_OpenID::arrayGet($args, 'auth_policies');
- if ($policies_str && $policies_str != "none") {
- $this->auth_policies = explode(" ", $policies_str);
- }
-
- $nist_level_str = Auth_OpenID::arrayGet($args, 'nist_auth_level');
- if ($nist_level_str !== null) {
- $nist_level = Auth_OpenID::intval($nist_level_str);
-
- if ($nist_level === false) {
- if ($strict) {
- return false;
- } else {
- $nist_level = null;
- }
- }
-
- if (0 <= $nist_level && $nist_level < 5) {
- $this->nist_auth_level = $nist_level;
- } else if ($strict) {
- return false;
- }
- }
-
- $auth_time = Auth_OpenID::arrayGet($args, 'auth_time');
- if ($auth_time !== null) {
- if (preg_match(PAPE_TIME_VALIDATOR, $auth_time)) {
- $this->auth_time = $auth_time;
- } else if ($strict) {
- return false;
- }
- }
- }
-
- function getExtensionArgs()
- {
- $ns_args = array();
- if (count($this->auth_policies) > 0) {
- $ns_args['auth_policies'] = implode(' ', $this->auth_policies);
- } else {
- $ns_args['auth_policies'] = 'none';
- }
-
- if ($this->nist_auth_level !== null) {
- if (!in_array($this->nist_auth_level, range(0, 4), true)) {
- return false;
- }
- $ns_args['nist_auth_level'] = strval($this->nist_auth_level);
- }
-
- if ($this->auth_time !== null) {
- if (!preg_match(PAPE_TIME_VALIDATOR, $this->auth_time)) {
- return false;
- }
-
- $ns_args['auth_time'] = $this->auth_time;
- }
-
- return $ns_args;
- }
-}
-
-
--- a/lib/openid-php/Auth/OpenID/Parse.php
+++ /dev/null
@@ -1,378 +1,1 @@
-<?php
-/**
- * This module implements a VERY limited parser that finds <link> tags
- * in the head of HTML or XHTML documents and parses out their
- * attributes according to the OpenID spec. It is a liberal parser,
- * but it requires these things from the data in order to work:
- *
- * - There must be an open <html> tag
- *
- * - There must be an open <head> tag inside of the <html> tag
- *
- * - Only <link>s that are found inside of the <head> tag are parsed
- * (this is by design)
- *
- * - The parser follows the OpenID specification in resolving the
- * attributes of the link tags. This means that the attributes DO
- * NOT get resolved as they would by an XML or HTML parser. In
- * particular, only certain entities get replaced, and href
- * attributes do not get resolved relative to a base URL.
- *
- * From http://openid.net/specs.bml:
- *
- * - The openid.server URL MUST be an absolute URL. OpenID consumers
- * MUST NOT attempt to resolve relative URLs.
- *
- * - The openid.server URL MUST NOT include entities other than &,
- * <, >, and ".
- *
- * The parser ignores SGML comments and <![CDATA[blocks]]>. Both kinds
- * of quoting are allowed for attributes.
- *
- * The parser deals with invalid markup in these ways:
- *
- * - Tag names are not case-sensitive
- *
- * - The <html> tag is accepted even when it is not at the top level
- *
- * - The <head> tag is accepted even when it is not a direct child of
- * the <html> tag, but a <html> tag must be an ancestor of the
- * <head> tag
- *
- * - <link> tags are accepted even when they are not direct children
- * of the <head> tag, but a <head> tag must be an ancestor of the
- * <link> tag
- *
- * - If there is no closing tag for an open <html> or <head> tag, the
- * remainder of the document is viewed as being inside of the
- * tag. If there is no closing tag for a <link> tag, the link tag is
- * treated as a short tag. Exceptions to this rule are that <html>
- * closes <html> and <body> or <head> closes <head>
- *
- * - Attributes of the <link> tag are not required to be quoted.
- *
- * - In the case of duplicated attribute names, the attribute coming
- * last in the tag will be the value returned.
- *
- * - Any text that does not parse as an attribute within a link tag
- * will be ignored. (e.g. <link pumpkin rel='openid.server' /> will
- * ignore pumpkin)
- *
- * - If there are more than one <html> or <head> tag, the parser only
- * looks inside of the first one.
- *
- * - The contents of <script> tags are ignored entirely, except
- * unclosed <script> tags. Unclosed <script> tags are ignored.
- *
- * - Any other invalid markup is ignored, including unclosed SGML
- * comments and unclosed <![CDATA[blocks.
- *
- * PHP versions 4 and 5
- *
- * LICENSE: See the COPYING file included in this distribution.
- *
- * @access private
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-
-/**
- * Require Auth_OpenID::arrayGet().
- */
-require_once "Auth/OpenID.php";
-
-class Auth_OpenID_Parse {
-
- /**
- * Specify some flags for use with regex matching.
- */
- var $_re_flags = "si";
-
- /**
- * Stuff to remove before we start looking for tags
- */
- var $_removed_re =
- "<!--.*?-->|<!\[CDATA\[.*?\]\]>|<script\b(?!:)[^>]*>.*?<\/script>";
-
- /**
- * Starts with the tag name at a word boundary, where the tag name
- * is not a namespace
- */
- var $_tag_expr = "<%s\b(?!:)([^>]*?)(?:\/>|>(.*)(?:<\/?%s\s*>|\Z))";
-
- var $_attr_find = '\b(\w+)=("[^"]*"|\'[^\']*\'|[^\'"\s\/<>]+)';
-
- var $_open_tag_expr = "<%s\b";
- var $_close_tag_expr = "<((\/%s\b)|(%s[^>\/]*\/))>";
-
- function Auth_OpenID_Parse()
- {
- $this->_link_find = sprintf("/<link\b(?!:)([^>]*)(?!<)>/%s",
- $this->_re_flags);
-
- $this->_entity_replacements = array(
- 'amp' => '&',
- 'lt' => '<',
- 'gt' => '>',
- 'quot' => '"'
- );
-
- $this->_attr_find = sprintf("/%s/%s",
- $this->_attr_find,
- $this->_re_flags);
-
- $this->_removed_re = sprintf("/%s/%s",
- $this->_removed_re,
- $this->_re_flags);
-
- $this->_ent_replace =
- sprintf("&(%s);", implode("|",
- $this->_entity_replacements));
- }
-
- /**
- * Returns a regular expression that will match a given tag in an
- * SGML string.
- */
- function tagMatcher($tag_name, $close_tags = null)
- {
- $expr = $this->_tag_expr;
-
- if ($close_tags) {
- $options = implode("|", array_merge(array($tag_name), $close_tags));
- $closer = sprintf("(?:%s)", $options);
- } else {
- $closer = $tag_name;
- }
-
- $expr = sprintf($expr, $tag_name, $closer);
- return sprintf("/%s/%s", $expr, $this->_re_flags);
- }
-
- function openTag($tag_name)
- {
- $expr = sprintf($this->_open_tag_expr, $tag_name);
- return sprintf("/%s/%s", $expr, $this->_re_flags);
- }
-
- function closeTag($tag_name)
- {
- $expr = sprintf($this->_close_tag_expr, $tag_name, $tag_name);
- return sprintf("/%s/%s", $expr, $this->_re_flags);
- }
-
- function htmlBegin($s)
- {
- $matches = array();
- $result = preg_match($this->openTag('html'), $s,
- $matches, PREG_OFFSET_CAPTURE);
- if ($result === false || !$matches) {
- return false;
- }
- // Return the offset of the first match.
- return $matches[0][1];
- }
-
- function htmlEnd($s)
- {
- $matches = array();
- $result = preg_match($this->closeTag('html'), $s,
- $matches, PREG_OFFSET_CAPTURE);
- if ($result === false || !$matches) {
- return false;
- }
- // Return the offset of the first match.
- return $matches[count($matches) - 1][1];
- }
-
- function headFind()
- {
- return $this->tagMatcher('head', array('body', 'html'));
- }
-
- function replaceEntities($str)
- {
- foreach ($this->_entity_replacements as $old => $new) {
- $str = preg_replace(sprintf("/&%s;/", $old), $new, $str);
- }
- return $str;
- }
-
- function removeQuotes($str)
- {
- $matches = array();
- $double = '/^"(.*)"$/';
- $single = "/^\'(.*)\'$/";
-
- if (preg_match($double, $str, $matches)) {
- return $matches[1];
- } else if (preg_match($single, $str, $matches)) {
- return $matches[1];
- } else {
- return $str;
- }
- }
-
- function match($regexp, $text, &$match)
- {
- if (!is_callable('mb_ereg_search_init')) {
- return preg_match($regexp, $text, $match);
- }
-
- $regexp = substr($regexp, 1, strlen($regexp) - 2 - strlen($this->_re_flags));
- mb_ereg_search_init($text);
- if (!mb_ereg_search($regexp)) {
- return false;
- }
- $match = mb_ereg_search_getregs();
- return true;
- }
-
- /**
- * Find all link tags in a string representing a HTML document and
- * return a list of their attributes.
- *
- * @todo This is quite ineffective and may fail with the default
- * pcre.backtrack_limit of 100000 in PHP 5.2, if $html is big.
- * It should rather use stripos (in PHP5) or strpos()+strtoupper()
- * in PHP4 to manage this.
- *
- * @param string $html The text to parse
- * @return array $list An array of arrays of attributes, one for each
- * link tag
- */
- function parseLinkAttrs($html)
- {
- $stripped = preg_replace($this->_removed_re,
- "",
- $html);
-
- $html_begin = $this->htmlBegin($stripped);
- $html_end = $this->htmlEnd($stripped);
-
- if ($html_begin === false) {
- return array();
- }
-
- if ($html_end === false) {
- $html_end = strlen($stripped);
- }
-
- $stripped = substr($stripped, $html_begin,
- $html_end - $html_begin);
-
- // Workaround to prevent PREG_BACKTRACK_LIMIT_ERROR:
- $old_btlimit = ini_set( 'pcre.backtrack_limit', -1 );
-
- // Try to find the <HEAD> tag.
- $head_re = $this->headFind();
- $head_match = array();
- if (!$this->match($head_re, $stripped, $head_match)) {
- ini_set( 'pcre.backtrack_limit', $old_btlimit );
- return array();
- }
-
- $link_data = array();
- $link_matches = array();
-
- if (!preg_match_all($this->_link_find, $head_match[0],
- $link_matches)) {
- ini_set( 'pcre.backtrack_limit', $old_btlimit );
- return array();
- }
-
- foreach ($link_matches[0] as $link) {
- $attr_matches = array();
- preg_match_all($this->_attr_find, $link, $attr_matches);
- $link_attrs = array();
- foreach ($attr_matches[0] as $index => $full_match) {
- $name = $attr_matches[1][$index];
- $value = $this->replaceEntities(
- $this->removeQuotes($attr_matches[2][$index]));
-
- $link_attrs[strtolower($name)] = $value;
- }
- $link_data[] = $link_attrs;
- }
-
- ini_set( 'pcre.backtrack_limit', $old_btlimit );
- return $link_data;
- }
-
- function relMatches($rel_attr, $target_rel)
- {
- // Does this target_rel appear in the rel_str?
- // XXX: TESTME
- $rels = preg_split("/\s+/", trim($rel_attr));
- foreach ($rels as $rel) {
- $rel = strtolower($rel);
- if ($rel == $target_rel) {
- return 1;
- }
- }
-
- return 0;
- }
-
- function linkHasRel($link_attrs, $target_rel)
- {
- // Does this link have target_rel as a relationship?
- // XXX: TESTME
- $rel_attr = Auth_OpeniD::arrayGet($link_attrs, 'rel', null);
- return ($rel_attr && $this->relMatches($rel_attr,
- $target_rel));
- }
-
- function findLinksRel($link_attrs_list, $target_rel)
- {
- // Filter the list of link attributes on whether it has
- // target_rel as a relationship.
- // XXX: TESTME
- $result = array();
- foreach ($link_attrs_list as $attr) {
- if ($this->linkHasRel($attr, $target_rel)) {
- $result[] = $attr;
- }
- }
-
- return $result;
- }
-
- function findFirstHref($link_attrs_list, $target_rel)
- {
- // Return the value of the href attribute for the first link
- // tag in the list that has target_rel as a relationship.
- // XXX: TESTME
- $matches = $this->findLinksRel($link_attrs_list,
- $target_rel);
- if (!$matches) {
- return null;
- }
- $first = $matches[0];
- return Auth_OpenID::arrayGet($first, 'href', null);
- }
-}
-
-function Auth_OpenID_legacy_discover($html_text, $server_rel,
- $delegate_rel)
-{
- $p = new Auth_OpenID_Parse();
-
- $link_attrs = $p->parseLinkAttrs($html_text);
-
- $server_url = $p->findFirstHref($link_attrs,
- $server_rel);
-
- if ($server_url === null) {
- return false;
- } else {
- $delegate_url = $p->findFirstHref($link_attrs,
- $delegate_rel);
- return array($delegate_url, $server_url);
- }
-}
-
-
--- a/lib/openid-php/Auth/OpenID/PostgreSQLStore.php
+++ /dev/null
@@ -1,113 +1,1 @@
-<?php
-/**
- * A PostgreSQL store.
- *
- * @package OpenID
- */
-
-/**
- * Require the base class file.
- */
-require_once "Auth/OpenID/SQLStore.php";
-
-/**
- * An SQL store that uses PostgreSQL as its backend.
- *
- * @package OpenID
- */
-class Auth_OpenID_PostgreSQLStore extends Auth_OpenID_SQLStore {
- /**
- * @access private
- */
- function setSQL()
- {
- $this->sql['nonce_table'] =
- "CREATE TABLE %s (server_url VARCHAR(2047) NOT NULL, ".
- "timestamp INTEGER NOT NULL, ".
- "salt CHAR(40) NOT NULL, ".
- "UNIQUE (server_url, timestamp, salt))";
-
- $this->sql['assoc_table'] =
- "CREATE TABLE %s (server_url VARCHAR(2047) NOT NULL, ".
- "handle VARCHAR(255) NOT NULL, ".
- "secret BYTEA NOT NULL, ".
- "issued INTEGER NOT NULL, ".
- "lifetime INTEGER NOT NULL, ".
- "assoc_type VARCHAR(64) NOT NULL, ".
- "PRIMARY KEY (server_url, handle), ".
- "CONSTRAINT secret_length_constraint CHECK ".
- "(LENGTH(secret) <= 128))";
-
- $this->sql['set_assoc'] =
- array(
- 'insert_assoc' => "INSERT INTO %s (server_url, handle, ".
- "secret, issued, lifetime, assoc_type) VALUES ".
- "(?, ?, '!', ?, ?, ?)",
- 'update_assoc' => "UPDATE %s SET secret = '!', issued = ?, ".
- "lifetime = ?, assoc_type = ? WHERE server_url = ? AND ".
- "handle = ?"
- );
-
- $this->sql['get_assocs'] =
- "SELECT handle, secret, issued, lifetime, assoc_type FROM %s ".
- "WHERE server_url = ?";
-
- $this->sql['get_assoc'] =
- "SELECT handle, secret, issued, lifetime, assoc_type FROM %s ".
- "WHERE server_url = ? AND handle = ?";
-
- $this->sql['remove_assoc'] =
- "DELETE FROM %s WHERE server_url = ? AND handle = ?";
-
- $this->sql['add_nonce'] =
- "INSERT INTO %s (server_url, timestamp, salt) VALUES ".
- "(?, ?, ?)"
- ;
-
- $this->sql['clean_nonce'] =
- "DELETE FROM %s WHERE timestamp < ?";
-
- $this->sql['clean_assoc'] =
- "DELETE FROM %s WHERE issued + lifetime < ?";
- }
-
- /**
- * @access private
- */
- function _set_assoc($server_url, $handle, $secret, $issued, $lifetime,
- $assoc_type)
- {
- $result = $this->_get_assoc($server_url, $handle);
- if ($result) {
- // Update the table since this associations already exists.
- $this->connection->query($this->sql['set_assoc']['update_assoc'],
- array($secret, $issued, $lifetime,
- $assoc_type, $server_url, $handle));
- } else {
- // Insert a new record because this association wasn't
- // found.
- $this->connection->query($this->sql['set_assoc']['insert_assoc'],
- array($server_url, $handle, $secret,
- $issued, $lifetime, $assoc_type));
- }
- }
-
- /**
- * @access private
- */
- function blobEncode($blob)
- {
- return $this->_octify($blob);
- }
-
- /**
- * @access private
- */
- function blobDecode($blob)
- {
- return $this->_unoctify($blob);
- }
-}
-
-
--- a/lib/openid-php/Auth/OpenID/SQLStore.php
+++ /dev/null
@@ -1,558 +1,1 @@
-<?php
-/**
- * SQL-backed OpenID stores.
- *
- * PHP versions 4 and 5
- *
- * LICENSE: See the COPYING file included in this distribution.
- *
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-
-/**
- * @access private
- */
-require_once 'Auth/OpenID/Interface.php';
-require_once 'Auth/OpenID/Nonce.php';
-
-/**
- * @access private
- */
-require_once 'Auth/OpenID.php';
-
-/**
- * @access private
- */
-require_once 'Auth/OpenID/Nonce.php';
-
-/**
- * This is the parent class for the SQL stores, which contains the
- * logic common to all of the SQL stores.
- *
- * The table names used are determined by the class variables
- * associations_table_name and nonces_table_name. To change the name
- * of the tables used, pass new table names into the constructor.
- *
- * To create the tables with the proper schema, see the createTables
- * method.
- *
- * This class shouldn't be used directly. Use one of its subclasses
- * instead, as those contain the code necessary to use a specific
- * database. If you're an OpenID integrator and you'd like to create
- * an SQL-driven store that wraps an application's database
- * abstraction, be sure to create a subclass of
- * {@link Auth_OpenID_DatabaseConnection} that calls the application's
- * database abstraction calls. Then, pass an instance of your new
- * database connection class to your SQLStore subclass constructor.
- *
- * All methods other than the constructor and createTables should be
- * considered implementation details.
- *
- * @package OpenID
- */
-class Auth_OpenID_SQLStore extends Auth_OpenID_OpenIDStore {
-
- /**
- * This creates a new SQLStore instance. It requires an
- * established database connection be given to it, and it allows
- * overriding the default table names.
- *
- * @param connection $connection This must be an established
- * connection to a database of the correct type for the SQLStore
- * subclass you're using. This must either be an PEAR DB
- * connection handle or an instance of a subclass of
- * Auth_OpenID_DatabaseConnection.
- *
- * @param associations_table: This is an optional parameter to
- * specify the name of the table used for storing associations.
- * The default value is 'oid_associations'.
- *
- * @param nonces_table: This is an optional parameter to specify
- * the name of the table used for storing nonces. The default
- * value is 'oid_nonces'.
- */
- function Auth_OpenID_SQLStore($connection,
- $associations_table = null,
- $nonces_table = null)
- {
- $this->associations_table_name = "oid_associations";
- $this->nonces_table_name = "oid_nonces";
-
- // Check the connection object type to be sure it's a PEAR
- // database connection.
- if (!(is_object($connection) &&
- (is_subclass_of($connection, 'db_common') ||
- is_subclass_of($connection,
- 'auth_openid_databaseconnection')))) {
- trigger_error("Auth_OpenID_SQLStore expected PEAR connection " .
- "object (got ".get_class($connection).")",
- E_USER_ERROR);
- return;
- }
-
- $this->connection = $connection;
-
- // Be sure to set the fetch mode so the results are keyed on
- // column name instead of column index. This is a PEAR
- // constant, so only try to use it if PEAR is present. Note
- // that Auth_Openid_Databaseconnection instances need not
- // implement ::setFetchMode for this reason.
- if (is_subclass_of($this->connection, 'db_common')) {
- $this->connection->setFetchMode(DB_FETCHMODE_ASSOC);
- }
-
- if ($associations_table) {
- $this->associations_table_name = $associations_table;
- }
-
- if ($nonces_table) {
- $this->nonces_table_name = $nonces_table;
- }
-
- $this->max_nonce_age = 6 * 60 * 60;
-
- // Be sure to run the database queries with auto-commit mode
- // turned OFF, because we want every function to run in a
- // transaction, implicitly. As a rule, methods named with a
- // leading underscore will NOT control transaction behavior.
- // Callers of these methods will worry about transactions.
- $this->connection->autoCommit(false);
-
- // Create an empty SQL strings array.
- $this->sql = array();
-
- // Call this method (which should be overridden by subclasses)
- // to populate the $this->sql array with SQL strings.
- $this->setSQL();
-
- // Verify that all required SQL statements have been set, and
- // raise an error if any expected SQL strings were either
- // absent or empty.
- list($missing, $empty) = $this->_verifySQL();
-
- if ($missing) {
- trigger_error("Expected keys in SQL query list: " .
- implode(", ", $missing),
- E_USER_ERROR);
- return;
- }
-
- if ($empty) {
- trigger_error("SQL list keys have no SQL strings: " .
- implode(", ", $empty),
- E_USER_ERROR);
- return;
- }
-
- // Add table names to queries.
- $this->_fixSQL();
- }
-
- function tableExists($table_name)
- {
- return !$this->isError(
- $this->connection->query(
- sprintf("SELECT * FROM %s LIMIT 0",
- $table_name)));
- }
-
- /**
- * Returns true if $value constitutes a database error; returns
- * false otherwise.
- */
- function isError($value)
- {
- return PEAR::isError($value);
- }
-
- /**
- * Converts a query result to a boolean. If the result is a
- * database error according to $this->isError(), this returns
- * false; otherwise, this returns true.
- */
- function resultToBool($obj)
- {
- if ($this->isError($obj)) {
- return false;
- } else {
- return true;
- }
- }
-
- /**
- * This method should be overridden by subclasses. This method is
- * called by the constructor to set values in $this->sql, which is
- * an array keyed on sql name.
- */
- function setSQL()
- {
- }
-
- /**
- * Resets the store by removing all records from the store's
- * tables.
- */
- function reset()
- {
- $this->connection->query(sprintf("DELETE FROM %s",
- $this->associations_table_name));
-
- $this->connection->query(sprintf("DELETE FROM %s",
- $this->nonces_table_name));
- }
-
- /**
- * @access private
- */
- function _verifySQL()
- {
- $missing = array();
- $empty = array();
-
- $required_sql_keys = array(
- 'nonce_table',
- 'assoc_table',
- 'set_assoc',
- 'get_assoc',
- 'get_assocs',
- 'remove_assoc'
- );
-
- foreach ($required_sql_keys as $key) {
- if (!array_key_exists($key, $this->sql)) {
- $missing[] = $key;
- } else if (!$this->sql[$key]) {
- $empty[] = $key;
- }
- }
-
- return array($missing, $empty);
- }
-
- /**
- * @access private
- */
- function _fixSQL()
- {
- $replacements = array(
- array(
- 'value' => $this->nonces_table_name,
- 'keys' => array('nonce_table',
- 'add_nonce',
- 'clean_nonce')
- ),
- array(
- 'value' => $this->associations_table_name,
- 'keys' => array('assoc_table',
- 'set_assoc',
- 'get_assoc',
- 'get_assocs',
- 'remove_assoc',
- 'clean_assoc')
- )
- );
-
- foreach ($replacements as $item) {
- $value = $item['value'];
- $keys = $item['keys'];
-
- foreach ($keys as $k) {
- if (is_array($this->sql[$k])) {
- foreach ($this->sql[$k] as $part_key => $part_value) {
- $this->sql[$k][$part_key] = sprintf($part_value,
- $value);
- }
- } else {
- $this->sql[$k] = sprintf($this->sql[$k], $value);
- }
- }
- }
- }
-
- function blobDecode($blob)
- {
- return $blob;
- }
-
- function blobEncode($str)
- {
- return $str;
- }
-
- function createTables()
- {
- $this->connection->autoCommit(true);
- $n = $this->create_nonce_table();
- $a = $this->create_assoc_table();
- $this->connection->autoCommit(false);
-
- if ($n && $a) {
- return true;
- } else {
- return false;
- }
- }
-
- function create_nonce_table()
- {
- if (!$this->tableExists($this->nonces_table_name)) {
- $r = $this->connection->query($this->sql['nonce_table']);
- return $this->resultToBool($r);
- }
- return true;
- }
-
- function create_assoc_table()
- {
- if (!$this->tableExists($this->associations_table_name)) {
- $r = $this->connection->query($this->sql['assoc_table']);
- return $this->resultToBool($r);
- }
- return true;
- }
-
- /**
- * @access private
- */
- function _set_assoc($server_url, $handle, $secret, $issued,
- $lifetime, $assoc_type)
- {
- return $this->connection->query($this->sql['set_assoc'],
- array(
- $server_url,
- $handle,
- $secret,
- $issued,
- $lifetime,
- $assoc_type));
- }
-
- function storeAssociation($server_url, $association)
- {
- if ($this->resultToBool($this->_set_assoc(
- $server_url,
- $association->handle,
- $this->blobEncode(
- $association->secret),
- $association->issued,
- $association->lifetime,
- $association->assoc_type
- ))) {
- $this->connection->commit();
- } else {
- $this->connection->rollback();
- }
- }
-
- /**
- * @access private
- */
- function _get_assoc($server_url, $handle)
- {
- $result = $this->connection->getRow($this->sql['get_assoc'],
- array($server_url, $handle));
- if ($this->isError($result)) {
- return null;
- } else {
- return $result;
- }
- }
-
- /**
- * @access private
- */
- function _get_assocs($server_url)
- {
- $result = $this->connection->getAll($this->sql['get_assocs'],
- array($server_url));
-
- if ($this->isError($result)) {
- return array();
- } else {
- return $result;
- }
- }
-
- function removeAssociation($server_url, $handle)
- {
- if ($this->_get_assoc($server_url, $handle) == null) {
- return false;
- }
-
- if ($this->resultToBool($this->connection->query(
- $this->sql['remove_assoc'],
- array($server_url, $handle)))) {
- $this->connection->commit();
- } else {
- $this->connection->rollback();
- }
-
- return true;
- }
-
- function getAssociation($server_url, $handle = null)
- {
- if ($handle !== null) {
- $assoc = $this->_get_assoc($server_url, $handle);
-
- $assocs = array();
- if ($assoc) {
- $assocs[] = $assoc;
- }
- } else {
- $assocs = $this->_get_assocs($server_url);
- }
-
- if (!$assocs || (count($assocs) == 0)) {
- return null;
- } else {
- $associations = array();
-
- foreach ($assocs as $assoc_row) {
- $assoc = new Auth_OpenID_Association($assoc_row['handle'],
- $assoc_row['secret'],
- $assoc_row['issued'],
- $assoc_row['lifetime'],
- $assoc_row['assoc_type']);
-
- $assoc->secret = $this->blobDecode($assoc->secret);
-
- if ($assoc->getExpiresIn() == 0) {
- $this->removeAssociation($server_url, $assoc->handle);
- } else {
- $associations[] = array($assoc->issued, $assoc);
- }
- }
-
- if ($associations) {
- $issued = array();
- $assocs = array();
- foreach ($associations as $key => $assoc) {
- $issued[$key] = $assoc[0];
- $assocs[$key] = $assoc[1];
- }
-
- array_multisort($issued, SORT_DESC, $assocs, SORT_DESC,
- $associations);
-
- // return the most recently issued one.
- list($issued, $assoc) = $associations[0];
- return $assoc;
- } else {
- return null;
- }
- }
- }
-
- /**
- * @access private
- */
- function _add_nonce($server_url, $timestamp, $salt)
- {
- $sql = $this->sql['add_nonce'];
- $result = $this->connection->query($sql, array($server_url,
- $timestamp,
- $salt));
- if ($this->isError($result)) {
- $this->connection->rollback();
- } else {
- $this->connection->commit();
- }
- return $this->resultToBool($result);
- }
-
- function useNonce($server_url, $timestamp, $salt)
- {
- global $Auth_OpenID_SKEW;
-
- if ( abs($timestamp - time()) > $Auth_OpenID_SKEW ) {
- return false;
- }
-
- return $this->_add_nonce($server_url, $timestamp, $salt);
- }
-
- /**
- * "Octifies" a binary string by returning a string with escaped
- * octal bytes. This is used for preparing binary data for
- * PostgreSQL BYTEA fields.
- *
- * @access private
- */
- function _octify($str)
- {
- $result = "";
- for ($i = 0; $i < Auth_OpenID::bytes($str); $i++) {
- $ch = substr($str, $i, 1);
- if ($ch == "\\") {
- $result .= "\\\\\\\\";
- } else if (ord($ch) == 0) {
- $result .= "\\\\000";
- } else {
- $result .= "\\" . strval(decoct(ord($ch)));
- }
- }
- return $result;
- }
-
- /**
- * "Unoctifies" octal-escaped data from PostgreSQL and returns the
- * resulting ASCII (possibly binary) string.
- *
- * @access private
- */
- function _unoctify($str)
- {
- $result = "";
- $i = 0;
- while ($i < strlen($str)) {
- $char = $str[$i];
- if ($char == "\\") {
- // Look to see if the next char is a backslash and
- // append it.
- if ($str[$i + 1] != "\\") {
- $octal_digits = substr($str, $i + 1, 3);
- $dec = octdec($octal_digits);
- $char = chr($dec);
- $i += 4;
- } else {
- $char = "\\";
- $i += 2;
- }
- } else {
- $i += 1;
- }
-
- $result .= $char;
- }
-
- return $result;
- }
-
- function cleanupNonces()
- {
- global $Auth_OpenID_SKEW;
- $v = time() - $Auth_OpenID_SKEW;
-
- $this->connection->query($this->sql['clean_nonce'], array($v));
- $num = $this->connection->affectedRows();
- $this->connection->commit();
- return $num;
- }
-
- function cleanupAssociations()
- {
- $this->connection->query($this->sql['clean_assoc'],
- array(time()));
- $num = $this->connection->affectedRows();
- $this->connection->commit();
- return $num;
- }
-}
-
-
-
--- a/lib/openid-php/Auth/OpenID/SQLiteStore.php
+++ /dev/null
@@ -1,71 +1,1 @@
-<?php
-/**
- * An SQLite store.
- *
- * @package OpenID
- */
-
-/**
- * Require the base class file.
- */
-require_once "Auth/OpenID/SQLStore.php";
-
-/**
- * An SQL store that uses SQLite as its backend.
- *
- * @package OpenID
- */
-class Auth_OpenID_SQLiteStore extends Auth_OpenID_SQLStore {
- function setSQL()
- {
- $this->sql['nonce_table'] =
- "CREATE TABLE %s (server_url VARCHAR(2047), timestamp INTEGER, ".
- "salt CHAR(40), UNIQUE (server_url, timestamp, salt))";
-
- $this->sql['assoc_table'] =
- "CREATE TABLE %s (server_url VARCHAR(2047), handle VARCHAR(255), ".
- "secret BLOB(128), issued INTEGER, lifetime INTEGER, ".
- "assoc_type VARCHAR(64), PRIMARY KEY (server_url, handle))";
-
- $this->sql['set_assoc'] =
- "INSERT OR REPLACE INTO %s VALUES (?, ?, ?, ?, ?, ?)";
-
- $this->sql['get_assocs'] =
- "SELECT handle, secret, issued, lifetime, assoc_type FROM %s ".
- "WHERE server_url = ?";
-
- $this->sql['get_assoc'] =
- "SELECT handle, secret, issued, lifetime, assoc_type FROM %s ".
- "WHERE server_url = ? AND handle = ?";
-
- $this->sql['remove_assoc'] =
- "DELETE FROM %s WHERE server_url = ? AND handle = ?";
-
- $this->sql['add_nonce'] =
- "INSERT INTO %s (server_url, timestamp, salt) VALUES (?, ?, ?)";
-
- $this->sql['clean_nonce'] =
- "DELETE FROM %s WHERE timestamp < ?";
-
- $this->sql['clean_assoc'] =
- "DELETE FROM %s WHERE issued + lifetime < ?";
- }
-
- /**
- * @access private
- */
- function _add_nonce($server_url, $timestamp, $salt)
- {
- // PECL SQLite extensions 1.0.3 and older (1.0.3 is the
- // current release at the time of this writing) have a broken
- // sqlite_escape_string function that breaks when passed the
- // empty string. Prefixing all strings with one character
- // keeps them unique and avoids this bug. The nonce table is
- // write-only, so we don't have to worry about updating other
- // functions with this same bad hack.
- return parent::_add_nonce('x' . $server_url, $timestamp, $salt);
- }
-}
-
-
--- a/lib/openid-php/Auth/OpenID/SReg.php
+++ /dev/null
@@ -1,522 +1,1 @@
-<?php
-/**
- * Simple registration request and response parsing and object
- * representation.
- *
- * This module contains objects representing simple registration
- * requests and responses that can be used with both OpenID relying
- * parties and OpenID providers.
- *
- * 1. The relying party creates a request object and adds it to the
- * {@link Auth_OpenID_AuthRequest} object before making the
- * checkid request to the OpenID provider:
- *
- * $sreg_req = Auth_OpenID_SRegRequest::build(array('email'));
- * $auth_request->addExtension($sreg_req);
- *
- * 2. The OpenID provider extracts the simple registration request
- * from the OpenID request using {@link
- * Auth_OpenID_SRegRequest::fromOpenIDRequest}, gets the user's
- * approval and data, creates an {@link Auth_OpenID_SRegResponse}
- * object and adds it to the id_res response:
- *
- * $sreg_req = Auth_OpenID_SRegRequest::fromOpenIDRequest(
- * $checkid_request);
- * // [ get the user's approval and data, informing the user that
- * // the fields in sreg_response were requested ]
- * $sreg_resp = Auth_OpenID_SRegResponse::extractResponse(
- * $sreg_req, $user_data);
- * $sreg_resp->toMessage($openid_response->fields);
- *
- * 3. The relying party uses {@link
- * Auth_OpenID_SRegResponse::fromSuccessResponse} to extract the data
- * from the OpenID response:
- *
- * $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse(
- * $success_response);
- *
- * @package OpenID
- */
-
-/**
- * Import message and extension internals.
- */
-require_once 'Auth/OpenID/Message.php';
-require_once 'Auth/OpenID/Extension.php';
-
-// The data fields that are listed in the sreg spec
-global $Auth_OpenID_sreg_data_fields;
-$Auth_OpenID_sreg_data_fields = array(
- 'fullname' => 'Full Name',
- 'nickname' => 'Nickname',
- 'dob' => 'Date of Birth',
- 'email' => 'E-mail Address',
- 'gender' => 'Gender',
- 'postcode' => 'Postal Code',
- 'country' => 'Country',
- 'language' => 'Language',
- 'timezone' => 'Time Zone');
-
-/**
- * Check to see that the given value is a valid simple registration
- * data field name. Return true if so, false if not.
- */
-function Auth_OpenID_checkFieldName($field_name)
-{
- global $Auth_OpenID_sreg_data_fields;
-
- if (!in_array($field_name, array_keys($Auth_OpenID_sreg_data_fields))) {
- return false;
- }
- return true;
-}
-
-// URI used in the wild for Yadis documents advertising simple
-// registration support
-define('Auth_OpenID_SREG_NS_URI_1_0', 'http://openid.net/sreg/1.0');
-
-// URI in the draft specification for simple registration 1.1
-// <http://openid.net/specs/openid-simple-registration-extension-1_1-01.html>
-define('Auth_OpenID_SREG_NS_URI_1_1', 'http://openid.net/extensions/sreg/1.1');
-
-// This attribute will always hold the preferred URI to use when
-// adding sreg support to an XRDS file or in an OpenID namespace
-// declaration.
-define('Auth_OpenID_SREG_NS_URI', Auth_OpenID_SREG_NS_URI_1_1);
-
-Auth_OpenID_registerNamespaceAlias(Auth_OpenID_SREG_NS_URI_1_1, 'sreg');
-
-/**
- * Does the given endpoint advertise support for simple
- * registration?
- *
- * $endpoint: The endpoint object as returned by OpenID discovery.
- * returns whether an sreg type was advertised by the endpoint
- */
-function Auth_OpenID_supportsSReg($endpoint)
-{
- return ($endpoint->usesExtension(Auth_OpenID_SREG_NS_URI_1_1) ||
- $endpoint->usesExtension(Auth_OpenID_SREG_NS_URI_1_0));
-}
-
-/**
- * A base class for classes dealing with Simple Registration protocol
- * messages.
- *
- * @package OpenID
- */
-class Auth_OpenID_SRegBase extends Auth_OpenID_Extension {
- /**
- * Extract the simple registration namespace URI from the given
- * OpenID message. Handles OpenID 1 and 2, as well as both sreg
- * namespace URIs found in the wild, as well as missing namespace
- * definitions (for OpenID 1)
- *
- * $message: The OpenID message from which to parse simple
- * registration fields. This may be a request or response message.
- *
- * Returns the sreg namespace URI for the supplied message. The
- * message may be modified to define a simple registration
- * namespace.
- *
- * @access private
- */
- static function _getSRegNS($message)
- {
- $alias = null;
- $found_ns_uri = null;
-
- // See if there exists an alias for one of the two defined
- // simple registration types.
- foreach (array(Auth_OpenID_SREG_NS_URI_1_1,
- Auth_OpenID_SREG_NS_URI_1_0) as $sreg_ns_uri) {
- $alias = $message->namespaces->getAlias($sreg_ns_uri);
- if ($alias !== null) {
- $found_ns_uri = $sreg_ns_uri;
- break;
- }
- }
-
- if ($alias === null) {
- // There is no alias for either of the types, so try to
- // add one. We default to using the modern value (1.1)
- $found_ns_uri = Auth_OpenID_SREG_NS_URI_1_1;
- if ($message->namespaces->addAlias(Auth_OpenID_SREG_NS_URI_1_1,
- 'sreg') === null) {
- // An alias for the string 'sreg' already exists, but
- // it's defined for something other than simple
- // registration
- return null;
- }
- }
-
- return $found_ns_uri;
- }
-}
-
-/**
- * An object to hold the state of a simple registration request.
- *
- * required: A list of the required fields in this simple registration
- * request
- *
- * optional: A list of the optional fields in this simple registration
- * request
- *
- * @package OpenID
- */
-class Auth_OpenID_SRegRequest extends Auth_OpenID_SRegBase {
-
- var $ns_alias = 'sreg';
-
- /**
- * Initialize an empty simple registration request.
- */
- static function build($required=null, $optional=null,
- $policy_url=null,
- $sreg_ns_uri=Auth_OpenID_SREG_NS_URI,
- $cls='Auth_OpenID_SRegRequest')
- {
- $obj = new $cls();
-
- $obj->required = array();
- $obj->optional = array();
- $obj->policy_url = $policy_url;
- $obj->ns_uri = $sreg_ns_uri;
-
- if ($required) {
- if (!$obj->requestFields($required, true, true)) {
- return null;
- }
- }
-
- if ($optional) {
- if (!$obj->requestFields($optional, false, true)) {
- return null;
- }
- }
-
- return $obj;
- }
-
- /**
- * Create a simple registration request that contains the fields
- * that were requested in the OpenID request with the given
- * arguments
- *
- * $request: The OpenID authentication request from which to
- * extract an sreg request.
- *
- * $cls: name of class to use when creating sreg request object.
- * Used for testing.
- *
- * Returns the newly created simple registration request
- */
- static function fromOpenIDRequest($request, $cls='Auth_OpenID_SRegRequest')
- {
-
- $obj = call_user_func_array(array($cls, 'build'),
- array(null, null, null, Auth_OpenID_SREG_NS_URI, $cls));
-
- // Since we're going to mess with namespace URI mapping, don't
- // mutate the object that was passed in.
- $m = $request->message;
-
- $obj->ns_uri = $obj->_getSRegNS($m);
- $args = $m->getArgs($obj->ns_uri);
-
- if ($args === null || Auth_OpenID::isFailure($args)) {
- return null;
- }
-
- $obj->parseExtensionArgs($args);
-
- return $obj;
- }
-
- /**
- * Parse the unqualified simple registration request parameters
- * and add them to this object.
- *
- * This method is essentially the inverse of
- * getExtensionArgs. This method restores the serialized simple
- * registration request fields.
- *
- * If you are extracting arguments from a standard OpenID
- * checkid_* request, you probably want to use fromOpenIDRequest,
- * which will extract the sreg namespace and arguments from the
- * OpenID request. This method is intended for cases where the
- * OpenID server needs more control over how the arguments are
- * parsed than that method provides.
- *
- * $args == $message->getArgs($ns_uri);
- * $request->parseExtensionArgs($args);
- *
- * $args: The unqualified simple registration arguments
- *
- * strict: Whether requests with fields that are not defined in
- * the simple registration specification should be tolerated (and
- * ignored)
- */
- function parseExtensionArgs($args, $strict=false)
- {
- foreach (array('required', 'optional') as $list_name) {
- $required = ($list_name == 'required');
- $items = Auth_OpenID::arrayGet($args, $list_name);
- if ($items) {
- foreach (explode(',', $items) as $field_name) {
- if (!$this->requestField($field_name, $required, $strict)) {
- if ($strict) {
- return false;
- }
- }
- }
- }
- }
-
- $this->policy_url = Auth_OpenID::arrayGet($args, 'policy_url');
-
- return true;
- }
-
- /**
- * A list of all of the simple registration fields that were
- * requested, whether they were required or optional.
- */
- function allRequestedFields()
- {
- return array_merge($this->required, $this->optional);
- }
-
- /**
- * Have any simple registration fields been requested?
- */
- function wereFieldsRequested()
- {
- return count($this->allRequestedFields());
- }
-
- /**
- * Was this field in the request?
- */
- function contains($field_name)
- {
- return (in_array($field_name, $this->required) ||
- in_array($field_name, $this->optional));
- }
-
- /**
- * Request the specified field from the OpenID user
- *
- * $field_name: the unqualified simple registration field name
- *
- * required: whether the given field should be presented to the
- * user as being a required to successfully complete the request
- *
- * strict: whether to raise an exception when a field is added to
- * a request more than once
- */
- function requestField($field_name,
- $required=false, $strict=false)
- {
- if (!Auth_OpenID_checkFieldName($field_name)) {
- return false;
- }
-
- if ($strict) {
- if ($this->contains($field_name)) {
- return false;
- }
- } else {
- if (in_array($field_name, $this->required)) {
- return true;
- }
-
- if (in_array($field_name, $this->optional)) {
- if ($required) {
- unset($this->optional[array_search($field_name,
- $this->optional)]);
- } else {
- return true;
- }
- }
- }
-
- if ($required) {
- $this->required[] = $field_name;
- } else {
- $this->optional[] = $field_name;
- }
-
- return true;
- }
-
- /**
- * Add the given list of fields to the request
- *
- * field_names: The simple registration data fields to request
- *
- * required: Whether these values should be presented to the user
- * as required
- *
- * strict: whether to raise an exception when a field is added to
- * a request more than once
- */
- function requestFields($field_names, $required=false, $strict=false)
- {
- if (!is_array($field_names)) {
- return false;
- }
-
- foreach ($field_names as $field_name) {
- if (!$this->requestField($field_name, $required, $strict=$strict)) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * Get a dictionary of unqualified simple registration arguments
- * representing this request.
- *
- * This method is essentially the inverse of
- * C{L{parseExtensionArgs}}. This method serializes the simple
- * registration request fields.
- */
- function getExtensionArgs()
- {
- $args = array();
-
- if ($this->required) {
- $args['required'] = implode(',', $this->required);
- }
-
- if ($this->optional) {
- $args['optional'] = implode(',', $this->optional);
- }
-
- if ($this->policy_url) {
- $args['policy_url'] = $this->policy_url;
- }
-
- return $args;
- }
-}
-
-/**
- * Represents the data returned in a simple registration response
- * inside of an OpenID C{id_res} response. This object will be created
- * by the OpenID server, added to the C{id_res} response object, and
- * then extracted from the C{id_res} message by the Consumer.
- *
- * @package OpenID
- */
-class Auth_OpenID_SRegResponse extends Auth_OpenID_SRegBase {
-
- var $ns_alias = 'sreg';
-
- function Auth_OpenID_SRegResponse($data=null,
- $sreg_ns_uri=Auth_OpenID_SREG_NS_URI)
- {
- if ($data === null) {
- $this->data = array();
- } else {
- $this->data = $data;
- }
-
- $this->ns_uri = $sreg_ns_uri;
- }
-
- /**
- * Take a C{L{SRegRequest}} and a dictionary of simple
- * registration values and create a C{L{SRegResponse}} object
- * containing that data.
- *
- * request: The simple registration request object
- *
- * data: The simple registration data for this response, as a
- * dictionary from unqualified simple registration field name to
- * string (unicode) value. For instance, the nickname should be
- * stored under the key 'nickname'.
- */
- static function extractResponse($request, $data)
- {
- $obj = new Auth_OpenID_SRegResponse();
- $obj->ns_uri = $request->ns_uri;
-
- foreach ($request->allRequestedFields() as $field) {
- $value = Auth_OpenID::arrayGet($data, $field);
- if ($value !== null) {
- $obj->data[$field] = $value;
- }
- }
-
- return $obj;
- }
-
- /**
- * Create a C{L{SRegResponse}} object from a successful OpenID
- * library response
- * (C{L{openid.consumer.consumer.SuccessResponse}}) response
- * message
- *
- * success_response: A SuccessResponse from consumer.complete()
- *
- * signed_only: Whether to process only data that was
- * signed in the id_res message from the server.
- *
- * Returns a simple registration response containing the data that
- * was supplied with the C{id_res} response.
- */
- static function fromSuccessResponse($success_response, $signed_only=true)
- {
- global $Auth_OpenID_sreg_data_fields;
-
- $obj = new Auth_OpenID_SRegResponse();
- $obj->ns_uri = $obj->_getSRegNS($success_response->message);
-
- if ($signed_only) {
- $args = $success_response->getSignedNS($obj->ns_uri);
- } else {
- $args = $success_response->message->getArgs($obj->ns_uri);
- }
-
- if ($args === null || Auth_OpenID::isFailure($args)) {
- return null;
- }
-
- foreach ($Auth_OpenID_sreg_data_fields as $field_name => $desc) {
- if (in_array($field_name, array_keys($args))) {
- $obj->data[$field_name] = $args[$field_name];
- }
- }
-
- return $obj;
- }
-
- function getExtensionArgs()
- {
- return $this->data;
- }
-
- // Read-only dictionary interface
- function get($field_name, $default=null)
- {
- if (!Auth_OpenID_checkFieldName($field_name)) {
- return null;
- }
-
- return Auth_OpenID::arrayGet($this->data, $field_name, $default);
- }
-
- function contents()
- {
- return $this->data;
- }
-}
-
-
-
--- a/lib/openid-php/Auth/OpenID/ServerRequest.php
+++ /dev/null
@@ -1,37 +1,1 @@
-<?php
-/**
- * OpenID Server Request
- *
- * @see Auth_OpenID_Server
- *
- * PHP versions 4 and 5
- *
- * LICENSE: See the COPYING file included in this distribution.
- *
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-/**
- * Imports
- */
-require_once "Auth/OpenID.php";
-
-/**
- * Object that holds the state of a request to the OpenID server
- *
- * With accessor functions to get at the internal request data.
- *
- * @see Auth_OpenID_Server
- * @package OpenID
- */
-class Auth_OpenID_ServerRequest {
- function Auth_OpenID_ServerRequest()
- {
- $this->mode = null;
- }
-}
-
-
--- a/lib/openid-php/Auth/OpenID/URINorm.php
+++ /dev/null
@@ -1,250 +1,1 @@
-<?php
-/**
- * URI normalization routines.
- *
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-
-require_once 'Auth/Yadis/Misc.php';
-
-// from appendix B of rfc 3986 (http://www.ietf.org/rfc/rfc3986.txt)
-function Auth_OpenID_getURIPattern()
-{
- return '&^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?&';
-}
-
-function Auth_OpenID_getAuthorityPattern()
-{
- return '/^([^@]*@)?([^:]*)(:.*)?/';
-}
-
-function Auth_OpenID_getEncodedPattern()
-{
- return '/%([0-9A-Fa-f]{2})/';
-}
-
-# gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
-#
-# sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
-# / "*" / "+" / "," / ";" / "="
-#
-# unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
-function Auth_OpenID_getURLIllegalCharRE()
-{
- return "/([^-A-Za-z0-9:\/\?#\[\]@\!\$&'\(\)\*\+,;=\._~\%])/";
-}
-
-function Auth_OpenID_getUnreserved()
-{
- $_unreserved = array();
- for ($i = 0; $i < 256; $i++) {
- $_unreserved[$i] = false;
- }
-
- for ($i = ord('A'); $i <= ord('Z'); $i++) {
- $_unreserved[$i] = true;
- }
-
- for ($i = ord('0'); $i <= ord('9'); $i++) {
- $_unreserved[$i] = true;
- }
-
- for ($i = ord('a'); $i <= ord('z'); $i++) {
- $_unreserved[$i] = true;
- }
-
- $_unreserved[ord('-')] = true;
- $_unreserved[ord('.')] = true;
- $_unreserved[ord('_')] = true;
- $_unreserved[ord('~')] = true;
-
- return $_unreserved;
-}
-
-function Auth_OpenID_getEscapeRE()
-{
- $parts = array();
- foreach (array_merge(Auth_Yadis_getUCSChars(),
- Auth_Yadis_getIPrivateChars()) as $pair) {
- list($m, $n) = $pair;
- $parts[] = sprintf("%s-%s", chr($m), chr($n));
- }
-
- return sprintf('[%s]', implode('', $parts));
-}
-
-function Auth_OpenID_pct_encoded_replace_unreserved($mo)
-{
- $_unreserved = Auth_OpenID_getUnreserved();
-
- $i = intval($mo[1], 16);
- if ($_unreserved[$i]) {
- return chr($i);
- } else {
- return strtoupper($mo[0]);
- }
-
- return $mo[0];
-}
-
-function Auth_OpenID_pct_encoded_replace($mo)
-{
- return chr(intval($mo[1], 16));
-}
-
-function Auth_OpenID_remove_dot_segments($path)
-{
- $result_segments = array();
-
- while ($path) {
- if (Auth_Yadis_startswith($path, '../')) {
- $path = substr($path, 3);
- } else if (Auth_Yadis_startswith($path, './')) {
- $path = substr($path, 2);
- } else if (Auth_Yadis_startswith($path, '/./')) {
- $path = substr($path, 2);
- } else if ($path == '/.') {
- $path = '/';
- } else if (Auth_Yadis_startswith($path, '/../')) {
- $path = substr($path, 3);
- if ($result_segments) {
- array_pop($result_segments);
- }
- } else if ($path == '/..') {
- $path = '/';
- if ($result_segments) {
- array_pop($result_segments);
- }
- } else if (($path == '..') ||
- ($path == '.')) {
- $path = '';
- } else {
- $i = 0;
- if ($path[0] == '/') {
- $i = 1;
- }
- $i = strpos($path, '/', $i);
- if ($i === false) {
- $i = strlen($path);
- }
- $result_segments[] = substr($path, 0, $i);
- $path = substr($path, $i);
- }
- }
-
- return implode('', $result_segments);
-}
-
-function Auth_OpenID_urinorm($uri)
-{
- $uri_matches = array();
- preg_match(Auth_OpenID_getURIPattern(), $uri, $uri_matches);
-
- if (count($uri_matches) < 9) {
- for ($i = count($uri_matches); $i <= 9; $i++) {
- $uri_matches[] = '';
- }
- }
-
- $illegal_matches = array();
- preg_match(Auth_OpenID_getURLIllegalCharRE(),
- $uri, $illegal_matches);
- if ($illegal_matches) {
- return null;
- }
-
- $scheme = $uri_matches[2];
- if ($scheme) {
- $scheme = strtolower($scheme);
- }
-
- $scheme = $uri_matches[2];
- if ($scheme === '') {
- // No scheme specified
- return null;
- }
-
- $scheme = strtolower($scheme);
- if (!in_array($scheme, array('http', 'https'))) {
- // Not an absolute HTTP or HTTPS URI
- return null;
- }
-
- $authority = $uri_matches[4];
- if ($authority === '') {
- // Not an absolute URI
- return null;
- }
-
- $authority_matches = array();
- preg_match(Auth_OpenID_getAuthorityPattern(),
- $authority, $authority_matches);
- if (count($authority_matches) === 0) {
- // URI does not have a valid authority
- return null;
- }
-
- if (count($authority_matches) < 4) {
- for ($i = count($authority_matches); $i <= 4; $i++) {
- $authority_matches[] = '';
- }
- }
-
- list($_whole, $userinfo, $host, $port) = $authority_matches;
-
- if ($userinfo === null) {
- $userinfo = '';
- }
-
- if (strpos($host, '%') !== -1) {
- $host = strtolower($host);
- $host = preg_replace_callback(
- Auth_OpenID_getEncodedPattern(),
- 'Auth_OpenID_pct_encoded_replace', $host);
- // NO IDNA.
- // $host = unicode($host, 'utf-8').encode('idna');
- } else {
- $host = strtolower($host);
- }
-
- if ($port) {
- if (($port == ':') ||
- ($scheme == 'http' && $port == ':80') ||
- ($scheme == 'https' && $port == ':443')) {
- $port = '';
- }
- } else {
- $port = '';
- }
-
- $authority = $userinfo . $host . $port;
-
- $path = $uri_matches[5];
- $path = preg_replace_callback(
- Auth_OpenID_getEncodedPattern(),
- 'Auth_OpenID_pct_encoded_replace_unreserved', $path);
-
- $path = Auth_OpenID_remove_dot_segments($path);
- if (!$path) {
- $path = '/';
- }
-
- $query = $uri_matches[6];
- if ($query === null) {
- $query = '';
- }
-
- $fragment = $uri_matches[8];
- if ($fragment === null) {
- $fragment = '';
- }
-
- return $scheme . '://' . $authority . $path . $query . $fragment;
-}
-
-
-
--- a/lib/openid-php/Auth/Yadis/HTTPFetcher.php
+++ /dev/null
@@ -1,175 +1,1 @@
-<?php
-/**
- * This module contains the HTTP fetcher interface
- *
- * PHP versions 4 and 5
- *
- * LICENSE: See the COPYING file included in this distribution.
- *
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-
-/**
- * Require logging functionality
- */
-require_once "Auth/OpenID.php";
-
-define('Auth_OpenID_FETCHER_MAX_RESPONSE_KB', 1024);
-define('Auth_OpenID_USER_AGENT',
- 'php-openid/'.Auth_OpenID_VERSION.' (php/'.phpversion().')');
-
-class Auth_Yadis_HTTPResponse {
- function Auth_Yadis_HTTPResponse($final_url = null, $status = null,
- $headers = null, $body = null)
- {
- $this->final_url = $final_url;
- $this->status = $status;
- $this->headers = $headers;
- $this->body = $body;
- }
-}
-
-/**
- * This class is the interface for HTTP fetchers the Yadis library
- * uses. This interface is only important if you need to write a new
- * fetcher for some reason.
- *
- * @access private
- * @package OpenID
- */
-class Auth_Yadis_HTTPFetcher {
-
- var $timeout = 20; // timeout in seconds.
-
- /**
- * Return whether a URL can be fetched. Returns false if the URL
- * scheme is not allowed or is not supported by this fetcher
- * implementation; returns true otherwise.
- *
- * @return bool
- */
- function canFetchURL($url)
- {
- if ($this->isHTTPS($url) && !$this->supportsSSL()) {
- Auth_OpenID::log("HTTPS URL unsupported fetching %s",
- $url);
- return false;
- }
-
- if (!$this->allowedURL($url)) {
- Auth_OpenID::log("URL fetching not allowed for '%s'",
- $url);
- return false;
- }
-
- return true;
- }
-
- /**
- * Return whether a URL should be allowed. Override this method to
- * conform to your local policy.
- *
- * By default, will attempt to fetch any http or https URL.
- */
- function allowedURL($url)
- {
- return $this->URLHasAllowedScheme($url);
- }
-
- /**
- * Does this fetcher implementation (and runtime) support fetching
- * HTTPS URLs? May inspect the runtime environment.
- *
- * @return bool $support True if this fetcher supports HTTPS
- * fetching; false if not.
- */
- function supportsSSL()
- {
- trigger_error("not implemented", E_USER_ERROR);
- }
-
- /**
- * Is this an https URL?
- *
- * @access private
- */
- function isHTTPS($url)
- {
- return (bool)preg_match('/^https:\/\//i', $url);
- }
-
- /**
- * Is this an http or https URL?
- *
- * @access private
- */
- function URLHasAllowedScheme($url)
- {
- return (bool)preg_match('/^https?:\/\//i', $url);
- }
-
- /**
- * @access private
- */
- function _findRedirect($headers, $url)
- {
- foreach ($headers as $line) {
- if (strpos(strtolower($line), "location: ") === 0) {
- $parts = explode(" ", $line, 2);
- $loc = $parts[1];
- $ppos = strpos($loc, "://");
- if ($ppos === false || $ppos > strpos($loc, "/")) {
- /* no host; add it */
- $hpos = strpos($url, "://");
- $prt = substr($url, 0, $hpos+3);
- $url = substr($url, $hpos+3);
- if (substr($loc, 0, 1) == "/") {
- /* absolute path */
- $fspos = strpos($url, "/");
- if ($fspos) $loc = $prt.substr($url, 0, $fspos).$loc;
- else $loc = $prt.$url.$loc;
- } else {
- /* relative path */
- $pp = $prt;
- while (1) {
- $xpos = strpos($url, "/");
- if ($xpos === false) break;
- $apos = strpos($url, "?");
- if ($apos !== false && $apos < $xpos) break;
- $apos = strpos($url, "&");
- if ($apos !== false && $apos < $xpos) break;
- $pp .= substr($url, 0, $xpos+1);
- $url = substr($url, $xpos+1);
- }
- $loc = $pp.$loc;
- }
- }
- return $loc;
- }
- }
- return null;
- }
-
- /**
- * Fetches the specified URL using optional extra headers and
- * returns the server's response.
- *
- * @param string $url The URL to be fetched.
- * @param array $extra_headers An array of header strings
- * (e.g. "Accept: text/html").
- * @return mixed $result An array of ($code, $url, $headers,
- * $body) if the URL could be fetched; null if the URL does not
- * pass the URLHasAllowedScheme check or if the server's response
- * is malformed.
- */
- function get($url, $headers = null)
- {
- trigger_error("not implemented", E_USER_ERROR);
- }
-}
-
-
--- a/lib/openid-php/Auth/Yadis/Misc.php
+++ /dev/null
@@ -1,59 +1,1 @@
-<?php
-/**
- * Miscellaneous utility values and functions for OpenID and Yadis.
- *
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-
-function Auth_Yadis_getUCSChars()
-{
- return array(
- array(0xA0, 0xD7FF),
- array(0xF900, 0xFDCF),
- array(0xFDF0, 0xFFEF),
- array(0x10000, 0x1FFFD),
- array(0x20000, 0x2FFFD),
- array(0x30000, 0x3FFFD),
- array(0x40000, 0x4FFFD),
- array(0x50000, 0x5FFFD),
- array(0x60000, 0x6FFFD),
- array(0x70000, 0x7FFFD),
- array(0x80000, 0x8FFFD),
- array(0x90000, 0x9FFFD),
- array(0xA0000, 0xAFFFD),
- array(0xB0000, 0xBFFFD),
- array(0xC0000, 0xCFFFD),
- array(0xD0000, 0xDFFFD),
- array(0xE1000, 0xEFFFD)
- );
-}
-
-function Auth_Yadis_getIPrivateChars()
-{
- return array(
- array(0xE000, 0xF8FF),
- array(0xF0000, 0xFFFFD),
- array(0x100000, 0x10FFFD)
- );
-}
-
-function Auth_Yadis_pct_escape_unicode($char_match)
-{
- $c = $char_match[0];
- $result = "";
- for ($i = 0; $i < strlen($c); $i++) {
- $result .= "%".sprintf("%X", ord($c[$i]));
- }
- return $result;
-}
-
-function Auth_Yadis_startswith($s, $stuff)
-{
- return strpos($s, $stuff) === 0;
-}
-
-
--- a/lib/openid-php/Auth/Yadis/ParanoidHTTPFetcher.php
+++ /dev/null
@@ -1,246 +1,1 @@
-<?php
-/**
- * This module contains the CURL-based HTTP fetcher implementation.
- *
- * PHP versions 4 and 5
- *
- * LICENSE: See the COPYING file included in this distribution.
- *
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-
-/**
- * Interface import
- */
-require_once "Auth/Yadis/HTTPFetcher.php";
-
-require_once "Auth/OpenID.php";
-
-/**
- * A paranoid {@link Auth_Yadis_HTTPFetcher} class which uses CURL
- * for fetching.
- *
- * @package OpenID
- */
-class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
- function Auth_Yadis_ParanoidHTTPFetcher()
- {
- $this->reset();
- }
-
- function reset()
- {
- $this->headers = array();
- $this->data = "";
- }
-
- /**
- * @access private
- */
- function _writeHeader($ch, $header)
- {
- array_push($this->headers, rtrim($header));
- return strlen($header);
- }
-
- /**
- * @access private
- */
- function _writeData($ch, $data)
- {
- if (strlen($this->data) > 1024*Auth_OpenID_FETCHER_MAX_RESPONSE_KB) {
- return 0;
- } else {
- $this->data .= $data;
- return strlen($data);
- }
- }
-
- /**
- * Does this fetcher support SSL URLs?
- */
- function supportsSSL()
- {
- $v = curl_version();
- if(is_array($v)) {
- return in_array('https', $v['protocols']);
- } elseif (is_string($v)) {
- return preg_match('/OpenSSL/i', $v);
- } else {
- return 0;
- }
- }
-
- function get($url, $extra_headers = null)
- {
- if (!$this->canFetchURL($url)) {
- return null;
- }
-
- $stop = time() + $this->timeout;
- $off = $this->timeout;
-
- $redir = true;
-
- while ($redir && ($off > 0)) {
- $this->reset();
-
- $c = curl_init();
-
- if ($c === false) {
- Auth_OpenID::log(
- "curl_init returned false; could not " .
- "initialize for URL '%s'", $url);
- return null;
- }
-
- if (defined('CURLOPT_NOSIGNAL')) {
- curl_setopt($c, CURLOPT_NOSIGNAL, true);
- }
-
- if (!$this->allowedURL($url)) {
- Auth_OpenID::log("Fetching URL not allowed: %s",
- $url);
- return null;
- }
-
- curl_setopt($c, CURLOPT_WRITEFUNCTION,
- array($this, "_writeData"));
- curl_setopt($c, CURLOPT_HEADERFUNCTION,
- array($this, "_writeHeader"));
-
- if ($extra_headers) {
- curl_setopt($c, CURLOPT_HTTPHEADER, $extra_headers);
- }
-
- $cv = curl_version();
- if(is_array($cv)) {
- $curl_user_agent = 'curl/'.$cv['version'];
- } else {
- $curl_user_agent = $cv;
- }
- curl_setopt($c, CURLOPT_USERAGENT,
- Auth_OpenID_USER_AGENT.' '.$curl_user_agent);
- curl_setopt($c, CURLOPT_TIMEOUT, $off);
- curl_setopt($c, CURLOPT_URL, $url);
-
- if (defined('Auth_OpenID_VERIFY_HOST')) {
- curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true);
- curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
- }
- curl_exec($c);
-
- $code = curl_getinfo($c, CURLINFO_HTTP_CODE);
- $body = $this->data;
- $headers = $this->headers;
-
- if (!$code) {
- Auth_OpenID::log("Got no response code when fetching %s", $url);
- Auth_OpenID::log("CURL error (%s): %s",
- curl_errno($c), curl_error($c));
- return null;
- }
-
- if (in_array($code, array(301, 302, 303, 307))) {
- $url = $this->_findRedirect($headers, $url);
- $redir = true;
- } else {
- $redir = false;
- curl_close($c);
-
- if (defined('Auth_OpenID_VERIFY_HOST') &&
- $this->isHTTPS($url)) {
- Auth_OpenID::log('OpenID: Verified SSL host %s using '.
- 'curl/get', $url);
- }
- $new_headers = array();
-
- foreach ($headers as $header) {
- if (strpos($header, ': ')) {
- list($name, $value) = explode(': ', $header, 2);
- $new_headers[$name] = $value;
- }
- }
-
- Auth_OpenID::log(
- "Successfully fetched '%s': GET response code %s",
- $url, $code);
-
- return new Auth_Yadis_HTTPResponse($url, $code,
- $new_headers, $body);
- }
-
- $off = $stop - time();
- }
-
- return null;
- }
-
- function post($url, $body, $extra_headers = null)
- {
- if (!$this->canFetchURL($url)) {
- return null;
- }
-
- $this->reset();
-
- $c = curl_init();
-
- if (defined('CURLOPT_NOSIGNAL')) {
- curl_setopt($c, CURLOPT_NOSIGNAL, true);
- }
-
- curl_setopt($c, CURLOPT_POST, true);
- curl_setopt($c, CURLOPT_POSTFIELDS, $body);
- curl_setopt($c, CURLOPT_TIMEOUT, $this->timeout);
- curl_setopt($c, CURLOPT_URL, $url);
- curl_setopt($c, CURLOPT_WRITEFUNCTION,
- array($this, "_writeData"));
-
- if (defined('Auth_OpenID_VERIFY_HOST')) {
- curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true);
- curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
- }
-
- curl_exec($c);
-
- $code = curl_getinfo($c, CURLINFO_HTTP_CODE);
-
- if (!$code) {
- Auth_OpenID::log("Got no response code when fetching %s", $url);
- Auth_OpenID::log("CURL error (%s): %s",
- curl_errno($c), curl_error($c));
- return null;
- }
-
- if (defined('Auth_OpenID_VERIFY_HOST') && $this->isHTTPS($url)) {
- Auth_OpenID::log('OpenID: Verified SSL host %s using '.
- 'curl/post', $url);
- }
- $body = $this->data;
-
- curl_close($c);
-
- $new_headers = $extra_headers;
-
- foreach ($this->headers as $header) {
- if (strpos($header, ': ')) {
- list($name, $value) = explode(': ', $header, 2);
- $new_headers[$name] = $value;
- }
-
- }
-
- Auth_OpenID::log("Successfully fetched '%s': POST response code %s",
- $url, $code);
-
- return new Auth_Yadis_HTTPResponse($url, $code,
- $new_headers, $body);
- }
-}
-
-
--- a/lib/openid-php/Auth/Yadis/ParseHTML.php
+++ /dev/null
@@ -1,259 +1,1 @@
-<?php
-/**
- * This is the HTML pseudo-parser for the Yadis library.
- *
- * PHP versions 4 and 5
- *
- * LICENSE: See the COPYING file included in this distribution.
- *
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-
-/**
- * This class is responsible for scanning an HTML string to find META
- * tags and their attributes. This is used by the Yadis discovery
- * process. This class must be instantiated to be used.
- *
- * @package OpenID
- */
-class Auth_Yadis_ParseHTML {
-
- /**
- * @access private
- */
- var $_re_flags = "si";
-
- /**
- * @access private
- */
- var $_removed_re =
- "<!--.*?-->|<!\[CDATA\[.*?\]\]>|<script\b(?!:)[^>]*>.*?<\/script>";
-
- /**
- * @access private
- */
- var $_tag_expr = "<%s%s(?:\s.*?)?%s>";
-
- /**
- * @access private
- */
- var $_attr_find = '\b([-\w]+)=(".*?"|\'.*?\'|.+?)[\/\s>]';
-
- function Auth_Yadis_ParseHTML()
- {
- $this->_attr_find = sprintf("/%s/%s",
- $this->_attr_find,
- $this->_re_flags);
-
- $this->_removed_re = sprintf("/%s/%s",
- $this->_removed_re,
- $this->_re_flags);
-
- $this->_entity_replacements = array(
- 'amp' => '&',
- 'lt' => '<',
- 'gt' => '>',
- 'quot' => '"'
- );
-
- $this->_ent_replace =
- sprintf("&(%s);", implode("|",
- $this->_entity_replacements));
- }
-
- /**
- * Replace HTML entities (amp, lt, gt, and quot) as well as
- * numeric entities (e.g. #x9f;) with their actual values and
- * return the new string.
- *
- * @access private
- * @param string $str The string in which to look for entities
- * @return string $new_str The new string entities decoded
- */
- function replaceEntities($str)
- {
- foreach ($this->_entity_replacements as $old => $new) {
- $str = preg_replace(sprintf("/&%s;/", $old), $new, $str);
- }
-
- // Replace numeric entities because html_entity_decode doesn't
- // do it for us.
- $str = preg_replace('~&#x([0-9a-f]+);~ei', 'chr(hexdec("\\1"))', $str);
- $str = preg_replace('~&#([0-9]+);~e', 'chr(\\1)', $str);
-
- return $str;
- }
-
- /**
- * Strip single and double quotes off of a string, if they are
- * present.
- *
- * @access private
- * @param string $str The original string
- * @return string $new_str The new string with leading and
- * trailing quotes removed
- */
- function removeQuotes($str)
- {
- $matches = array();
- $double = '/^"(.*)"$/';
- $single = "/^\'(.*)\'$/";
-
- if (preg_match($double, $str, $matches)) {
- return $matches[1];
- } else if (preg_match($single, $str, $matches)) {
- return $matches[1];
- } else {
- return $str;
- }
- }
-
- /**
- * Create a regular expression that will match an opening
- * or closing tag from a set of names.
- *
- * @access private
- * @param mixed $tag_names Tag names to match
- * @param mixed $close false/0 = no, true/1 = yes, other = maybe
- * @param mixed $self_close false/0 = no, true/1 = yes, other = maybe
- * @return string $regex A regular expression string to be used
- * in, say, preg_match.
- */
- function tagPattern($tag_names, $close, $self_close)
- {
- if (is_array($tag_names)) {
- $tag_names = '(?:'.implode('|',$tag_names).')';
- }
- if ($close) {
- $close = '\/' . (($close == 1)? '' : '?');
- } else {
- $close = '';
- }
- if ($self_close) {
- $self_close = '(?:\/\s*)' . (($self_close == 1)? '' : '?');
- } else {
- $self_close = '';
- }
- $expr = sprintf($this->_tag_expr, $close, $tag_names, $self_close);
-
- return sprintf("/%s/%s", $expr, $this->_re_flags);
- }
-
- /**
- * Given an HTML document string, this finds all the META tags in
- * the document, provided they are found in the
- * <HTML><HEAD>...</HEAD> section of the document. The <HTML> tag
- * may be missing.
- *
- * @access private
- * @param string $html_string An HTMl document string
- * @return array $tag_list Array of tags; each tag is an array of
- * attribute -> value.
- */
- function getMetaTags($html_string)
- {
- $html_string = preg_replace($this->_removed_re,
- "",
- $html_string);
-
- $key_tags = array($this->tagPattern('html', false, false),
- $this->tagPattern('head', false, false),
- $this->tagPattern('head', true, false),
- $this->tagPattern('html', true, false),
- $this->tagPattern(array(
- 'body', 'frameset', 'frame', 'p', 'div',
- 'table','span','a'), 'maybe', 'maybe'));
- $key_tags_pos = array();
- foreach ($key_tags as $pat) {
- $matches = array();
- preg_match($pat, $html_string, $matches, PREG_OFFSET_CAPTURE);
- if($matches) {
- $key_tags_pos[] = $matches[0][1];
- } else {
- $key_tags_pos[] = null;
- }
- }
- // no opening head tag
- if (is_null($key_tags_pos[1])) {
- return array();
- }
- // the effective </head> is the min of the following
- if (is_null($key_tags_pos[2])) {
- $key_tags_pos[2] = strlen($html_string);
- }
- foreach (array($key_tags_pos[3], $key_tags_pos[4]) as $pos) {
- if (!is_null($pos) && $pos < $key_tags_pos[2]) {
- $key_tags_pos[2] = $pos;
- }
- }
- // closing head tag comes before opening head tag
- if ($key_tags_pos[1] > $key_tags_pos[2]) {
- return array();
- }
- // if there is an opening html tag, make sure the opening head tag
- // comes after it
- if (!is_null($key_tags_pos[0]) && $key_tags_pos[1] < $key_tags_pos[0]) {
- return array();
- }
- $html_string = substr($html_string, $key_tags_pos[1],
- ($key_tags_pos[2]-$key_tags_pos[1]));
-
- $link_data = array();
- $link_matches = array();
-
- if (!preg_match_all($this->tagPattern('meta', false, 'maybe'),
- $html_string, $link_matches)) {
- return array();
- }
-
- foreach ($link_matches[0] as $link) {
- $attr_matches = array();
- preg_match_all($this->_attr_find, $link, $attr_matches);
- $link_attrs = array();
- foreach ($attr_matches[0] as $index => $full_match) {
- $name = $attr_matches[1][$index];
- $value = $this->replaceEntities(
- $this->removeQuotes($attr_matches[2][$index]));
-
- $link_attrs[strtolower($name)] = $value;
- }
- $link_data[] = $link_attrs;
- }
-
- return $link_data;
- }
-
- /**
- * Looks for a META tag with an "http-equiv" attribute whose value
- * is one of ("x-xrds-location", "x-yadis-location"), ignoring
- * case. If such a META tag is found, its "content" attribute
- * value is returned.
- *
- * @param string $html_string An HTML document in string format
- * @return mixed $content The "content" attribute value of the
- * META tag, if found, or null if no such tag was found.
- */
- function getHTTPEquiv($html_string)
- {
- $meta_tags = $this->getMetaTags($html_string);
-
- if ($meta_tags) {
- foreach ($meta_tags as $tag) {
- if (array_key_exists('http-equiv', $tag) &&
- (in_array(strtolower($tag['http-equiv']),
- array('x-xrds-location', 'x-yadis-location'))) &&
- array_key_exists('content', $tag)) {
- return $tag['content'];
- }
- }
- }
-
- return null;
- }
-}
-
-
--- a/lib/openid-php/Auth/Yadis/PlainHTTPFetcher.php
+++ /dev/null
@@ -1,249 +1,1 @@
-<?php
-/**
- * This module contains the plain non-curl HTTP fetcher
- * implementation.
- *
- * PHP versions 4 and 5
- *
- * LICENSE: See the COPYING file included in this distribution.
- *
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-
-/**
- * Interface import
- */
-require_once "Auth/Yadis/HTTPFetcher.php";
-
-/**
- * This class implements a plain, hand-built socket-based fetcher
- * which will be used in the event that CURL is unavailable.
- *
- * @package OpenID
- */
-class Auth_Yadis_PlainHTTPFetcher extends Auth_Yadis_HTTPFetcher {
- /**
- * Does this fetcher support SSL URLs?
- */
- function supportsSSL()
- {
- return function_exists('openssl_open');
- }
-
- function get($url, $extra_headers = null)
- {
- if (!$this->canFetchURL($url)) {
- return null;
- }
-
- $redir = true;
-
- $stop = time() + $this->timeout;
- $off = $this->timeout;
-
- while ($redir && ($off > 0)) {
-
- $parts = parse_url($url);
-
- $specify_port = true;
-
- // Set a default port.
- if (!array_key_exists('port', $parts)) {
- $specify_port = false;
- if ($parts['scheme'] == 'http') {
- $parts['port'] = 80;
- } elseif ($parts['scheme'] == 'https') {
- $parts['port'] = 443;
- } else {
- return null;
- }
- }
-
- if (!array_key_exists('path', $parts)) {
- $parts['path'] = '/';
- }
-
- $host = $parts['host'];
-
- if ($parts['scheme'] == 'https') {
- $host = 'ssl://' . $host;
- }
-
- $user_agent = Auth_OpenID_USER_AGENT;
-
- $headers = array(
- "GET ".$parts['path'].
- (array_key_exists('query', $parts) ?
- "?".$parts['query'] : "").
- " HTTP/1.0",
- "User-Agent: $user_agent",
- "Host: ".$parts['host'].
- ($specify_port ? ":".$parts['port'] : ""),
- "Port: ".$parts['port']);
-
- $errno = 0;
- $errstr = '';
-
- if ($extra_headers) {
- foreach ($extra_headers as $h) {
- $headers[] = $h;
- }
- }
-
- @$sock = fsockopen($host, $parts['port'], $errno, $errstr,
- $this->timeout);
- if ($sock === false) {
- return false;
- }
-
- stream_set_timeout($sock, $this->timeout);
-
- fputs($sock, implode("\r\n", $headers) . "\r\n\r\n");
-
- $data = "";
- $kilobytes = 0;
- while (!feof($sock) &&
- $kilobytes < Auth_OpenID_FETCHER_MAX_RESPONSE_KB ) {
- $data .= fgets($sock, 1024);
- $kilobytes += 1;
- }
-
- fclose($sock);
-
- // Split response into header and body sections
- list($headers, $body) = explode("\r\n\r\n", $data, 2);
- $headers = explode("\r\n", $headers);
-
- $http_code = explode(" ", $headers[0]);
- $code = $http_code[1];
-
- if (in_array($code, array('301', '302'))) {
- $url = $this->_findRedirect($headers, $url);
- $redir = true;
- } else {
- $redir = false;
- }
-
- $off = $stop - time();
- }
-
- $new_headers = array();
-
- foreach ($headers as $header) {
- if (preg_match("/:/", $header)) {
- $parts = explode(": ", $header, 2);
-
- if (count($parts) == 2) {
- list($name, $value) = $parts;
- $new_headers[$name] = $value;
- }
- }
-
- }
-
- return new Auth_Yadis_HTTPResponse($url, $code, $new_headers, $body);
- }
-
- function post($url, $body, $extra_headers = null)
- {
- if (!$this->canFetchURL($url)) {
- return null;
- }
-
- $parts = parse_url($url);
-
- $headers = array();
-
- $post_path = $parts['path'];
- if (isset($parts['query'])) {
- $post_path .= '?' . $parts['query'];
- }
-
- $headers[] = "POST ".$post_path." HTTP/1.0";
- $headers[] = "Host: " . $parts['host'];
- $headers[] = "Content-type: application/x-www-form-urlencoded";
- $headers[] = "Content-length: " . strval(strlen($body));
-
- if ($extra_headers &&
- is_array($extra_headers)) {
- $headers = array_merge($headers, $extra_headers);
- }
-
- // Join all headers together.
- $all_headers = implode("\r\n", $headers);
-
- // Add headers, two newlines, and request body.
- $request = $all_headers . "\r\n\r\n" . $body;
-
- // Set a default port.
- if (!array_key_exists('port', $parts)) {
- if ($parts['scheme'] == 'http') {
- $parts['port'] = 80;
- } elseif ($parts['scheme'] == 'https') {
- $parts['port'] = 443;
- } else {
- return null;
- }
- }
-
- if ($parts['scheme'] == 'https') {
- $parts['host'] = sprintf("ssl://%s", $parts['host']);
- }
-
- // Connect to the remote server.
- $errno = 0;
- $errstr = '';
-
- $sock = fsockopen($parts['host'], $parts['port'], $errno, $errstr,
- $this->timeout);
-
- if ($sock === false) {
- return null;
- }
-
- stream_set_timeout($sock, $this->timeout);
-
- // Write the POST request.
- fputs($sock, $request);
-
- // Get the response from the server.
- $response = "";
- while (!feof($sock)) {
- if ($data = fgets($sock, 128)) {
- $response .= $data;
- } else {
- break;
- }
- }
-
- // Split the request into headers and body.
- list($headers, $response_body) = explode("\r\n\r\n", $response, 2);
-
- $headers = explode("\r\n", $headers);
-
- // Expect the first line of the headers data to be something
- // like HTTP/1.1 200 OK. Split the line on spaces and take
- // the second token, which should be the return code.
- $http_code = explode(" ", $headers[0]);
- $code = $http_code[1];
-
- $new_headers = array();
-
- foreach ($headers as $header) {
- if (preg_match("/:/", $header)) {
- list($name, $value) = explode(": ", $header, 2);
- $new_headers[$name] = $value;
- }
-
- }
-
- return new Auth_Yadis_HTTPResponse($url, $code,
- $new_headers, $response_body);
- }
-}
-
-
--- a/lib/openid-php/Auth/Yadis/XML.php
+++ /dev/null
@@ -1,353 +1,1 @@
-<?php
-/**
- * XML-parsing classes to wrap the domxml and DOM extensions for PHP 4
- * and 5, respectively.
- *
- * @package OpenID
- */
-
-/**
- * The base class for wrappers for available PHP XML-parsing
- * extensions. To work with this Yadis library, subclasses of this
- * class MUST implement the API as defined in the remarks for this
- * class. Subclasses of Auth_Yadis_XMLParser are used to wrap
- * particular PHP XML extensions such as 'domxml'. These are used
- * internally by the library depending on the availability of
- * supported PHP XML extensions.
- *
- * @package OpenID
- */
-class Auth_Yadis_XMLParser {
- /**
- * Initialize an instance of Auth_Yadis_XMLParser with some
- * XML and namespaces. This SHOULD NOT be overridden by
- * subclasses.
- *
- * @param string $xml_string A string of XML to be parsed.
- * @param array $namespace_map An array of ($ns_name => $ns_uri)
- * to be registered with the XML parser. May be empty.
- * @return boolean $result True if the initialization and
- * namespace registration(s) succeeded; false otherwise.
- */
- function init($xml_string, $namespace_map)
- {
- if (!$this->setXML($xml_string)) {
- return false;
- }
-
- foreach ($namespace_map as $prefix => $uri) {
- if (!$this->registerNamespace($prefix, $uri)) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * Register a namespace with the XML parser. This should be
- * overridden by subclasses.
- *
- * @param string $prefix The namespace prefix to appear in XML tag
- * names.
- *
- * @param string $uri The namespace URI to be used to identify the
- * namespace in the XML.
- *
- * @return boolean $result True if the registration succeeded;
- * false otherwise.
- */
- function registerNamespace($prefix, $uri)
- {
- // Not implemented.
- }
-
- /**
- * Set this parser object's XML payload. This should be
- * overridden by subclasses.
- *
- * @param string $xml_string The XML string to pass to this
- * object's XML parser.
- *
- * @return boolean $result True if the initialization succeeded;
- * false otherwise.
- */
- function setXML($xml_string)
- {
- // Not implemented.
- }
-
- /**
- * Evaluate an XPath expression and return the resulting node
- * list. This should be overridden by subclasses.
- *
- * @param string $xpath The XPath expression to be evaluated.
- *
- * @param mixed $node A node object resulting from a previous
- * evalXPath call. This node, if specified, provides the context
- * for the evaluation of this xpath expression.
- *
- * @return array $node_list An array of matching opaque node
- * objects to be used with other methods of this parser class.
- */
- function &evalXPath($xpath, $node = null)
- {
- // Not implemented.
- }
-
- /**
- * Return the textual content of a specified node.
- *
- * @param mixed $node A node object from a previous call to
- * $this->evalXPath().
- *
- * @return string $content The content of this node.
- */
- function content($node)
- {
- // Not implemented.
- }
-
- /**
- * Return the attributes of a specified node.
- *
- * @param mixed $node A node object from a previous call to
- * $this->evalXPath().
- *
- * @return array $attrs An array mapping attribute names to
- * values.
- */
- function attributes($node)
- {
- // Not implemented.
- }
-}
-
-/**
- * This concrete implementation of Auth_Yadis_XMLParser implements
- * the appropriate API for the 'domxml' extension which is typically
- * packaged with PHP 4. This class will be used whenever the 'domxml'
- * extension is detected. See the Auth_Yadis_XMLParser class for
- * details on this class's methods.
- *
- * @package OpenID
- */
-class Auth_Yadis_domxml extends Auth_Yadis_XMLParser {
- function Auth_Yadis_domxml()
- {
- $this->xml = null;
- $this->doc = null;
- $this->xpath = null;
- $this->errors = array();
- }
-
- function setXML($xml_string)
- {
- $this->xml = $xml_string;
- $this->doc = @domxml_open_mem($xml_string, DOMXML_LOAD_PARSING,
- $this->errors);
-
- if (!$this->doc) {
- return false;
- }
-
- $this->xpath = $this->doc->xpath_new_context();
-
- return true;
- }
-
- function registerNamespace($prefix, $uri)
- {
- return xpath_register_ns($this->xpath, $prefix, $uri);
- }
-
- function &evalXPath($xpath, $node = null)
- {
- if ($node) {
- $result = @$this->xpath->xpath_eval($xpath, $node);
- } else {
- $result = @$this->xpath->xpath_eval($xpath);
- }
-
- if (!$result) {
- $n = array();
- return $n;
- }
-
- if (!$result->nodeset) {
- $n = array();
- return $n;
- }
-
- return $result->nodeset;
- }
-
- function content($node)
- {
- if ($node) {
- return $node->get_content();
- }
- }
-
- function attributes($node)
- {
- if ($node) {
- $arr = $node->attributes();
- $result = array();
-
- if ($arr) {
- foreach ($arr as $attrnode) {
- $result[$attrnode->name] = $attrnode->value;
- }
- }
-
- return $result;
- }
- }
-}
-
-/**
- * This concrete implementation of Auth_Yadis_XMLParser implements
- * the appropriate API for the 'dom' extension which is typically
- * packaged with PHP 5. This class will be used whenever the 'dom'
- * extension is detected. See the Auth_Yadis_XMLParser class for
- * details on this class's methods.
- *
- * @package OpenID
- */
-class Auth_Yadis_dom extends Auth_Yadis_XMLParser {
- function Auth_Yadis_dom()
- {
- $this->xml = null;
- $this->doc = null;
- $this->xpath = null;
- $this->errors = array();
- }
-
- function setXML($xml_string)
- {
- $this->xml = $xml_string;
- $this->doc = new DOMDocument;
-
- if (!$this->doc) {
- return false;
- }
-
- if (!@$this->doc->loadXML($xml_string)) {
- return false;
- }
-
- $this->xpath = new DOMXPath($this->doc);
-
- if ($this->xpath) {
- return true;
- } else {
- return false;
- }
- }
-
- function registerNamespace($prefix, $uri)
- {
- return $this->xpath->registerNamespace($prefix, $uri);
- }
-
- function &evalXPath($xpath, $node = null)
- {
- if ($node) {
- $result = @$this->xpath->query($xpath, $node);
- } else {
- $result = @$this->xpath->query($xpath);
- }
-
- $n = array();
-
- if (!$result) {
- return $n;
- }
-
- for ($i = 0; $i < $result->length; $i++) {
- $n[] = $result->item($i);
- }
-
- return $n;
- }
-
- function content($node)
- {
- if ($node) {
- return $node->textContent;
- }
- }
-
- function attributes($node)
- {
- if ($node) {
- $arr = $node->attributes;
- $result = array();
-
- if ($arr) {
- for ($i = 0; $i < $arr->length; $i++) {
- $node = $arr->item($i);
- $result[$node->nodeName] = $node->nodeValue;
- }
- }
-
- return $result;
- }
- }
-}
-
-global $__Auth_Yadis_defaultParser;
-$__Auth_Yadis_defaultParser = null;
-
-/**
- * Set a default parser to override the extension-driven selection of
- * available parser classes. This is helpful in a test environment or
- * one in which multiple parsers can be used but one is more
- * desirable.
- *
- * @param Auth_Yadis_XMLParser $parser An instance of a
- * Auth_Yadis_XMLParser subclass.
- */
-function Auth_Yadis_setDefaultParser($parser)
-{
- global $__Auth_Yadis_defaultParser;
- $__Auth_Yadis_defaultParser = $parser;
-}
-
-function Auth_Yadis_getSupportedExtensions()
-{
- return array('dom' => 'Auth_Yadis_dom',
- 'domxml' => 'Auth_Yadis_domxml');
-}
-
-/**
- * Returns an instance of a Auth_Yadis_XMLParser subclass based on
- * the availability of PHP extensions for XML parsing. If
- * Auth_Yadis_setDefaultParser has been called, the parser used in
- * that call will be returned instead.
- */
-function Auth_Yadis_getXMLParser()
-{
- global $__Auth_Yadis_defaultParser;
-
- if (isset($__Auth_Yadis_defaultParser)) {
- return $__Auth_Yadis_defaultParser;
- }
-
- foreach(Auth_Yadis_getSupportedExtensions() as $extension => $classname)
- {
- if (extension_loaded($extension))
- {
- $p = new $classname();
- Auth_Yadis_setDefaultParser($p);
- return $p;
- }
- }
-
- return false;
-}
-
-
-
--- a/lib/openid-php/Auth/Yadis/XRI.php
+++ /dev/null
@@ -1,235 +1,1 @@
-<?php
-/**
- * Routines for XRI resolution.
- *
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-
-require_once 'Auth/Yadis/Misc.php';
-require_once 'Auth/Yadis/Yadis.php';
-require_once 'Auth/OpenID.php';
-
-function Auth_Yadis_getDefaultProxy()
-{
- return 'http://xri.net/';
-}
-
-function Auth_Yadis_getXRIAuthorities()
-{
- return array('!', '=', '@', '+', '$', '(');
-}
-
-function Auth_Yadis_getEscapeRE()
-{
- $parts = array();
- foreach (array_merge(Auth_Yadis_getUCSChars(),
- Auth_Yadis_getIPrivateChars()) as $pair) {
- list($m, $n) = $pair;
- $parts[] = sprintf("%s-%s", chr($m), chr($n));
- }
-
- return sprintf('/[%s]/', implode('', $parts));
-}
-
-function Auth_Yadis_getXrefRE()
-{
- return '/\((.*?)\)/';
-}
-
-function Auth_Yadis_identifierScheme($identifier)
-{
- if (Auth_Yadis_startswith($identifier, 'xri://') ||
- ($identifier &&
- in_array($identifier[0], Auth_Yadis_getXRIAuthorities()))) {
- return "XRI";
- } else {
- return "URI";
- }
-}
-
-function Auth_Yadis_toIRINormal($xri)
-{
- if (!Auth_Yadis_startswith($xri, 'xri://')) {
- $xri = 'xri://' . $xri;
- }
-
- return Auth_Yadis_escapeForIRI($xri);
-}
-
-function _escape_xref($xref_match)
-{
- $xref = $xref_match[0];
- $xref = str_replace('/', '%2F', $xref);
- $xref = str_replace('?', '%3F', $xref);
- $xref = str_replace('#', '%23', $xref);
- return $xref;
-}
-
-function Auth_Yadis_escapeForIRI($xri)
-{
- $xri = str_replace('%', '%25', $xri);
- $xri = preg_replace_callback(Auth_Yadis_getXrefRE(),
- '_escape_xref', $xri);
- return $xri;
-}
-
-function Auth_Yadis_toURINormal($xri)
-{
- return Auth_Yadis_iriToURI(Auth_Yadis_toIRINormal($xri));
-}
-
-function Auth_Yadis_iriToURI($iri)
-{
- if (1) {
- return $iri;
- } else {
- // According to RFC 3987, section 3.1, "Mapping of IRIs to URIs"
- return preg_replace_callback(Auth_Yadis_getEscapeRE(),
- 'Auth_Yadis_pct_escape_unicode', $iri);
- }
-}
-
-
-function Auth_Yadis_XRIAppendArgs($url, $args)
-{
- // Append some arguments to an HTTP query. Yes, this is just like
- // OpenID's appendArgs, but with special seasoning for XRI
- // queries.
-
- if (count($args) == 0) {
- return $url;
- }
-
- // Non-empty array; if it is an array of arrays, use multisort;
- // otherwise use sort.
- if (array_key_exists(0, $args) &&
- is_array($args[0])) {
- // Do nothing here.
- } else {
- $keys = array_keys($args);
- sort($keys);
- $new_args = array();
- foreach ($keys as $key) {
- $new_args[] = array($key, $args[$key]);
- }
- $args = $new_args;
- }
-
- // According to XRI Resolution section "QXRI query parameters":
- //
- // "If the original QXRI had a null query component (only a
- // leading question mark), or a query component consisting of
- // only question marks, one additional leading question mark MUST
- // be added when adding any XRI resolution parameters."
- if (strpos(rtrim($url, '?'), '?') !== false) {
- $sep = '&';
- } else {
- $sep = '?';
- }
-
- return $url . $sep . Auth_OpenID::httpBuildQuery($args);
-}
-
-function Auth_Yadis_providerIsAuthoritative($providerID, $canonicalID)
-{
- $lastbang = strrpos($canonicalID, '!');
- $p = substr($canonicalID, 0, $lastbang);
- return $p == $providerID;
-}
-
-function Auth_Yadis_rootAuthority($xri)
-{
- // Return the root authority for an XRI.
-
- $root = null;
-
- if (Auth_Yadis_startswith($xri, 'xri://')) {
- $xri = substr($xri, 6);
- }
-
- $authority = explode('/', $xri, 2);
- $authority = $authority[0];
- if ($authority[0] == '(') {
- // Cross-reference.
- // XXX: This is incorrect if someone nests cross-references so
- // there is another close-paren in there. Hopefully nobody
- // does that before we have a real xriparse function.
- // Hopefully nobody does that *ever*.
- $root = substr($authority, 0, strpos($authority, ')') + 1);
- } else if (in_array($authority[0], Auth_Yadis_getXRIAuthorities())) {
- // Other XRI reference.
- $root = $authority[0];
- } else {
- // IRI reference.
- $_segments = explode("!", $authority);
- $segments = array();
- foreach ($_segments as $s) {
- $segments = array_merge($segments, explode("*", $s));
- }
- $root = $segments[0];
- }
-
- return Auth_Yadis_XRI($root);
-}
-
-function Auth_Yadis_XRI($xri)
-{
- if (!Auth_Yadis_startswith($xri, 'xri://')) {
- $xri = 'xri://' . $xri;
- }
- return $xri;
-}
-
-function Auth_Yadis_getCanonicalID($iname, $xrds)
-{
- // Returns false or a canonical ID value.
-
- // Now nodes are in reverse order.
- $xrd_list = array_reverse($xrds->allXrdNodes);
- $parser = $xrds->parser;
- $node = $xrd_list[0];
-
- $canonicalID_nodes = $parser->evalXPath('xrd:CanonicalID', $node);
-
- if (!$canonicalID_nodes) {
- return false;
- }
-
- $canonicalID = $canonicalID_nodes[0];
- $canonicalID = Auth_Yadis_XRI($parser->content($canonicalID));
-
- $childID = $canonicalID;
-
- for ($i = 1; $i < count($xrd_list); $i++) {
- $xrd = $xrd_list[$i];
-
- $parent_sought = substr($childID, 0, strrpos($childID, '!'));
- $parentCID = $parser->evalXPath('xrd:CanonicalID', $xrd);
- if (!$parentCID) {
- return false;
- }
- $parentCID = Auth_Yadis_XRI($parser->content($parentCID[0]));
-
- if (strcasecmp($parent_sought, $parentCID)) {
- // raise XRDSFraud.
- return false;
- }
-
- $childID = $parent_sought;
- }
-
- $root = Auth_Yadis_rootAuthority($iname);
- if (!Auth_Yadis_providerIsAuthoritative($root, $childID)) {
- // raise XRDSFraud.
- return false;
- }
-
- return $canonicalID;
-}
-
-
-
--- a/lib/openid-php/Auth/Yadis/XRIRes.php
+++ /dev/null
@@ -1,73 +1,1 @@
-<?php
-/**
- * Code for using a proxy XRI resolver.
- */
-
-require_once 'Auth/Yadis/XRDS.php';
-require_once 'Auth/Yadis/XRI.php';
-
-class Auth_Yadis_ProxyResolver {
- function Auth_Yadis_ProxyResolver($fetcher, $proxy_url = null)
- {
- $this->fetcher = $fetcher;
- $this->proxy_url = $proxy_url;
- if (!$this->proxy_url) {
- $this->proxy_url = Auth_Yadis_getDefaultProxy();
- }
- }
-
- function queryURL($xri, $service_type = null)
- {
- // trim off the xri:// prefix
- $qxri = substr(Auth_Yadis_toURINormal($xri), 6);
- $hxri = $this->proxy_url . $qxri;
- $args = array(
- '_xrd_r' => 'application/xrds+xml'
- );
-
- if ($service_type) {
- $args['_xrd_t'] = $service_type;
- } else {
- // Don't perform service endpoint selection.
- $args['_xrd_r'] .= ';sep=false';
- }
-
- $query = Auth_Yadis_XRIAppendArgs($hxri, $args);
- return $query;
- }
-
- function query($xri, $service_types, $filters = array())
- {
- $services = array();
- $canonicalID = null;
- foreach ($service_types as $service_type) {
- $url = $this->queryURL($xri, $service_type);
- $response = $this->fetcher->get($url);
- if ($response->status != 200 and $response->status != 206) {
- continue;
- }
- $xrds = Auth_Yadis_XRDS::parseXRDS($response->body);
- if (!$xrds) {
- continue;
- }
- $canonicalID = Auth_Yadis_getCanonicalID($xri,
- $xrds);
-
- if ($canonicalID === false) {
- return null;
- }
-
- $some_services = $xrds->services($filters);
- $services = array_merge($services, $some_services);
- // TODO:
- // * If we do get hits for multiple service_types, we're
- // almost certainly going to have duplicated service
- // entries and broken priority ordering.
- }
- return array($canonicalID, $services);
- }
-}
-
-
-
--- a/lib/openid-php/Auth/Yadis/Yadis.php
+++ /dev/null
@@ -1,383 +1,1 @@
-<?php
-/**
- * The core PHP Yadis implementation.
- *
- * PHP versions 4 and 5
- *
- * LICENSE: See the COPYING file included in this distribution.
- *
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005-2008 Janrain, Inc.
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
- */
-
-/**
- * Need both fetcher types so we can use the right one based on the
- * presence or absence of CURL.
- */
-require_once "Auth/Yadis/PlainHTTPFetcher.php";
-require_once "Auth/Yadis/ParanoidHTTPFetcher.php";
-
-/**
- * Need this for parsing HTML (looking for META tags).
- */
-require_once "Auth/Yadis/ParseHTML.php";
-
-/**
- * Need this to parse the XRDS document during Yadis discovery.
- */
-require_once "Auth/Yadis/XRDS.php";
-
-/**
- * XRDS (yadis) content type
- */
-define('Auth_Yadis_CONTENT_TYPE', 'application/xrds+xml');
-
-/**
- * Yadis header
- */
-define('Auth_Yadis_HEADER_NAME', 'X-XRDS-Location');
-
-/**
- * Contains the result of performing Yadis discovery on a URI.
- *
- * @package OpenID
- */
-class Auth_Yadis_DiscoveryResult {
-
- // The URI that was passed to the fetcher
- var $request_uri = null;
-
- // The result of following redirects from the request_uri
- var $normalized_uri = null;
-
- // The URI from which the response text was returned (set to
- // None if there was no XRDS document found)
- var $xrds_uri = null;
-
- var $xrds = null;
-
- // The content-type returned with the response_text
- var $content_type = null;
-
- // The document returned from the xrds_uri
- var $response_text = null;
-
- // Did the discovery fail miserably?
- var $failed = false;
-
- function Auth_Yadis_DiscoveryResult($request_uri)
- {
- // Initialize the state of the object
- // sets all attributes to None except the request_uri
- $this->request_uri = $request_uri;
- }
-
- function fail()
- {
- $this->failed = true;
- }
-
- function isFailure()
- {
- return $this->failed;
- }
-
- /**
- * Returns the list of service objects as described by the XRDS
- * document, if this yadis object represents a successful Yadis
- * discovery.
- *
- * @return array $services An array of {@link Auth_Yadis_Service}
- * objects
- */
- function services()
- {
- if ($this->xrds) {
- return $this->xrds->services();
- }
-
- return null;
- }
-
- function usedYadisLocation()
- {
- // Was the Yadis protocol's indirection used?
- return ($this->xrds_uri && $this->normalized_uri != $this->xrds_uri);
- }
-
- function isXRDS()
- {
- // Is the response text supposed to be an XRDS document?
- return ($this->usedYadisLocation() ||
- $this->content_type == Auth_Yadis_CONTENT_TYPE);
- }
-}
-
-/**
- *
- * Perform the Yadis protocol on the input URL and return an iterable
- * of resulting endpoint objects.
- *
- * input_url: The URL on which to perform the Yadis protocol
- *
- * @return: The normalized identity URL and an iterable of endpoint
- * objects generated by the filter function.
- *
- * xrds_parse_func: a callback which will take (uri, xrds_text) and
- * return an array of service endpoint objects or null. Usually
- * array('Auth_OpenID_ServiceEndpoint', 'fromXRDS').
- *
- * discover_func: if not null, a callback which should take (uri) and
- * return an Auth_Yadis_Yadis object or null.
- */
-function Auth_Yadis_getServiceEndpoints($input_url, $xrds_parse_func,
- $discover_func=null, $fetcher=null)
-{
- if ($discover_func === null) {
- $discover_function = array('Auth_Yadis_Yadis', 'discover');
- }
-
- $yadis_result = call_user_func_array($discover_func,
- array($input_url, &$fetcher));
-
- if ($yadis_result === null) {
- return array($input_url, array());
- }
-
- $endpoints = call_user_func_array($xrds_parse_func,
- array($yadis_result->normalized_uri,
- $yadis_result->response_text));
-
- if ($endpoints === null) {
- $endpoints = array();
- }
-
- return array($yadis_result->normalized_uri, $endpoints);
-}
-
-/**
- * This is the core of the PHP Yadis library. This is the only class
- * a user needs to use to perform Yadis discovery. This class
- * performs the discovery AND stores the result of the discovery.
- *
- * First, require this library into your program source:
- *
- * <pre> require_once "Auth/Yadis/Yadis.php";</pre>
- *
- * To perform Yadis discovery, first call the "discover" method
- * statically with a URI parameter:
- *
- * <pre> $http_response = array();
- * $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
- * $yadis_object = Auth_Yadis_Yadis::discover($uri,
- * $http_response, $fetcher);</pre>
- *
- * If the discovery succeeds, $yadis_object will be an instance of
- * {@link Auth_Yadis_Yadis}. If not, it will be null. The XRDS
- * document found during discovery should have service descriptions,
- * which can be accessed by calling
- *
- * <pre> $service_list = $yadis_object->services();</pre>
- *
- * which returns an array of objects which describe each service.
- * These objects are instances of Auth_Yadis_Service. Each object
- * describes exactly one whole Service element, complete with all of
- * its Types and URIs (no expansion is performed). The common use
- * case for using the service objects returned by services() is to
- * write one or more filter functions and pass those to services():
- *
- * <pre> $service_list = $yadis_object->services(
- * array("filterByURI",
- * "filterByExtension"));</pre>
- *
- * The filter functions (whose names appear in the array passed to
- * services()) take the following form:
- *
- * <pre> function myFilter($service) {
- * // Query $service object here. Return true if the service
- * // matches your query; false if not.
- * }</pre>
- *
- * This is an example of a filter which uses a regular expression to
- * match the content of URI tags (note that the Auth_Yadis_Service
- * class provides a getURIs() method which you should use instead of
- * this contrived example):
- *
- * <pre>
- * function URIMatcher($service) {
- * foreach ($service->getElements('xrd:URI') as $uri) {
- * if (preg_match("/some_pattern/",
- * $service->parser->content($uri))) {
- * return true;
- * }
- * }
- * return false;
- * }</pre>
- *
- * The filter functions you pass will be called for each service
- * object to determine which ones match the criteria your filters
- * specify. The default behavior is that if a given service object
- * matches ANY of the filters specified in the services() call, it
- * will be returned. You can specify that a given service object will
- * be returned ONLY if it matches ALL specified filters by changing
- * the match mode of services():
- *
- * <pre> $yadis_object->services(array("filter1", "filter2"),
- * SERVICES_YADIS_MATCH_ALL);</pre>
- *
- * See {@link SERVICES_YADIS_MATCH_ALL} and {@link
- * SERVICES_YADIS_MATCH_ANY}.
- *
- * Services described in an XRDS should have a library which you'll
- * probably be using. Those libraries are responsible for defining
- * filters that can be used with the "services()" call. If you need
- * to write your own filter, see the documentation for {@link
- * Auth_Yadis_Service}.
- *
- * @package OpenID
- */
-class Auth_Yadis_Yadis {
-
- /**
- * Returns an HTTP fetcher object. If the CURL extension is
- * present, an instance of {@link Auth_Yadis_ParanoidHTTPFetcher}
- * is returned. If not, an instance of
- * {@link Auth_Yadis_PlainHTTPFetcher} is returned.
- *
- * If Auth_Yadis_CURL_OVERRIDE is defined, this method will always
- * return a {@link Auth_Yadis_PlainHTTPFetcher}.
- */
- static function getHTTPFetcher($timeout = 20)
- {
- if (Auth_Yadis_Yadis::curlPresent() &&
- (!defined('Auth_Yadis_CURL_OVERRIDE'))) {
- $fetcher = new Auth_Yadis_ParanoidHTTPFetcher($timeout);
- } else {
- $fetcher = new Auth_Yadis_PlainHTTPFetcher($timeout);
- }
- return $fetcher;
- }
-
- static function curlPresent()
- {
- return function_exists('curl_init');
- }
-
- /**
- * @access private
- */
- static function _getHeader($header_list, $names)
- {
- foreach ($header_list as $name => $value) {
- foreach ($names as $n) {
- if (strtolower($name) == strtolower($n)) {
- return $value;
- }
- }
- }
-
- return null;
- }
-
- /**
- * @access private
- */
- static function _getContentType($content_type_header)
- {
- if ($content_type_header) {
- $parts = explode(";", $content_type_header);
- return strtolower($parts[0]);
- }
- }
-
- /**
- * This should be called statically and will build a Yadis
- * instance if the discovery process succeeds. This implements
- * Yadis discovery as specified in the Yadis specification.
- *
- * @param string $uri The URI on which to perform Yadis discovery.
- *
- * @param array $http_response An array reference where the HTTP
- * response object will be stored (see {@link
- * Auth_Yadis_HTTPResponse}.
- *
- * @param Auth_Yadis_HTTPFetcher $fetcher An instance of a
- * Auth_Yadis_HTTPFetcher subclass.
- *
- * @param array $extra_ns_map An array which maps namespace names
- * to namespace URIs to be used when parsing the Yadis XRDS
- * document.
- *
- * @param integer $timeout An optional fetcher timeout, in seconds.
- *
- * @return mixed $obj Either null or an instance of
- * Auth_Yadis_Yadis, depending on whether the discovery
- * succeeded.
- */
- static function discover($uri, $fetcher,
- $extra_ns_map = null, $timeout = 20)
- {
- $result = new Auth_Yadis_DiscoveryResult($uri);
-
- $request_uri = $uri;
- $headers = array("Accept: " . Auth_Yadis_CONTENT_TYPE .
- ', text/html; q=0.3, application/xhtml+xml; q=0.5');
-
- if ($fetcher === null) {
- $fetcher = Auth_Yadis_Yadis::getHTTPFetcher($timeout);
- }
-
- $response = $fetcher->get($uri, $headers);
-
- if (!$response || ($response->status != 200 and
- $response->status != 206)) {
- $result->fail();
- return $result;
- }
-
- $result->normalized_uri = $response->final_url;
- $result->content_type = Auth_Yadis_Yadis::_getHeader(
- $response->headers,
- array('content-type'));
-
- if ($result->content_type &&
- (Auth_Yadis_Yadis::_getContentType($result->content_type) ==
- Auth_Yadis_CONTENT_TYPE)) {
- $result->xrds_uri = $result->normalized_uri;
- } else {
- $yadis_location = Auth_Yadis_Yadis::_getHeader(
- $response->headers,
- array(Auth_Yadis_HEADER_NAME));
-
- if (!$yadis_location) {
- $parser = new Auth_Yadis_ParseHTML();
- $yadis_location = $parser->getHTTPEquiv($response->body);
- }
-
- if ($yadis_location) {
- $result->xrds_uri = $yadis_location;
-
- $response = $fetcher->get($yadis_location);
-
- if ((!$response) || ($response->status != 200 and
- $response->status != 206)) {
- $result->fail();
- return $result;
- }
-
- $result->content_type = Auth_Yadis_Yadis::_getHeader(
- $response->headers,
- array('content-type'));
- }
- }
-
- $result->response_text = $response->body;
- return $result;
- }
-}
-
-
-
--- /dev/null
+++ b/lib/openid.php
@@ -1,1 +1,782 @@
-
+<?php
+/**
+ * This class provides a simple interface for OpenID (1.1 and 2.0) authentication.
+ * Supports Yadis discovery.
+ * The authentication process is stateless/dumb.
+ *
+ * Usage:
+ * Sign-on with OpenID is a two step process:
+ * Step one is authentication with the provider:
+ * <code>
+ * $openid = new LightOpenID('my-host.example.org');
+ * $openid->identity = 'ID supplied by user';
+ * header('Location: ' . $openid->authUrl());
+ * </code>
+ * The provider then sends various parameters via GET, one of them is openid_mode.
+ * Step two is verification:
+ * <code>
+ * if ($this->data['openid_mode']) {
+ * $openid = new LightOpenID('my-host.example.org');
+ * echo $openid->validate() ? 'Logged in.' : 'Failed';
+ * }
+ * </code>
+ *
+ * Change the 'my-host.example.org' to your domain name. Do NOT use $_SERVER['HTTP_HOST']
+ * for that, unless you know what you are doing.
+ *
+ * Optionally, you can set $returnUrl and $realm (or $trustRoot, which is an alias).
+ * The default values for those are:
+ * $openid->realm = (!empty($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'];
+ * $openid->returnUrl = $openid->realm . $_SERVER['REQUEST_URI'];
+ * If you don't know their meaning, refer to any openid tutorial, or specification. Or just guess.
+ *
+ * AX and SREG extensions are supported.
+ * To use them, specify $openid->required and/or $openid->optional before calling $openid->authUrl().
+ * These are arrays, with values being AX schema paths (the 'path' part of the URL).
+ * For example:
+ * $openid->required = array('namePerson/friendly', 'contact/email');
+ * $openid->optional = array('namePerson/first');
+ * If the server supports only SREG or OpenID 1.1, these are automaticaly
+ * mapped to SREG names, so that user doesn't have to know anything about the server.
+ *
+ * To get the values, use $openid->getAttributes().
+ *
+ *
+ * The library requires PHP >= 5.1.2 with curl or http/https stream wrappers enabled.
+ * @author Mewp
+ * @copyright Copyright (c) 2010, Mewp
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ */
+class LightOpenID
+{
+ public $returnUrl
+ , $required = array()
+ , $optional = array()
+ , $verify_peer = null
+ , $capath = null
+ , $cainfo = null
+ , $data;
+ private $identity, $claimed_id;
+ protected $server, $version, $trustRoot, $aliases, $identifier_select = false
+ , $ax = false, $sreg = false, $setup_url = null;
+ static protected $ax_to_sreg = array(
+ 'namePerson/friendly' => 'nickname',
+ 'contact/email' => 'email',
+ 'namePerson' => 'fullname',
+ 'birthDate' => 'dob',
+ 'person/gender' => 'gender',
+ 'contact/postalCode/home' => 'postcode',
+ 'contact/country/home' => 'country',
+ 'pref/language' => 'language',
+ 'pref/timezone' => 'timezone',
+ );
+
+ function __construct($host)
+ {
+ $this->trustRoot = (strpos($host, '://') ? $host : 'http://' . $host);
+ if ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')
+ || (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
+ && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
+ ) {
+ $this->trustRoot = (strpos($host, '://') ? $host : 'https://' . $host);
+ }
+
+ if(($host_end = strpos($this->trustRoot, '/', 8)) !== false) {
+ $this->trustRoot = substr($this->trustRoot, 0, $host_end);
+ }
+
+ $uri = rtrim(preg_replace('#((?<=\?)|&)openid\.[^&]+#', '', $_SERVER['REQUEST_URI']), '?');
+ $this->returnUrl = $this->trustRoot . $uri;
+
+ $this->data = ($_SERVER['REQUEST_METHOD'] === 'POST') ? $_POST : $_GET;
+
+ if(!function_exists('curl_init') && !in_array('https', stream_get_wrappers())) {
+ throw new ErrorException('You must have either https wrappers or curl enabled.');
+ }
+ }
+
+ function __set($name, $value)
+ {
+ switch ($name) {
+ case 'identity':
+ if (strlen($value = trim((String) $value))) {
+ if (preg_match('#^xri:/*#i', $value, $m)) {
+ $value = substr($value, strlen($m[0]));
+ } elseif (!preg_match('/^(?:[=@+\$!\(]|https?:)/i', $value)) {
+ $value = "http://$value";
+ }
+ if (preg_match('#^https?://[^/]+$#i', $value, $m)) {
+ $value .= '/';
+ }
+ }
+ $this->$name = $this->claimed_id = $value;
+ break;
+ case 'trustRoot':
+ case 'realm':
+ $this->trustRoot = trim($value);
+ }
+ }
+
+ function __get($name)
+ {
+ switch ($name) {
+ case 'identity':
+ # We return claimed_id instead of identity,
+ # because the developer should see the claimed identifier,
+ # i.e. what he set as identity, not the op-local identifier (which is what we verify)
+ return $this->claimed_id;
+ case 'trustRoot':
+ case 'realm':
+ return $this->trustRoot;
+ case 'mode':
+ return empty($this->data['openid_mode']) ? null : $this->data['openid_mode'];
+ }
+ }
+
+ /**
+ * Checks if the server specified in the url exists.
+ *
+ * @param $url url to check
+ * @return true, if the server exists; false otherwise
+ */
+ function hostExists($url)
+ {
+ if (strpos($url, '/') === false) {
+ $server = $url;
+ } else {
+ $server = @parse_url($url, PHP_URL_HOST);
+ }
+
+ if (!$server) {
+ return false;
+ }
+
+ return !!gethostbynamel($server);
+ }
+
+ protected function request_curl($url, $method='GET', $params=array())
+ {
+ $params = http_build_query($params, '', '&');
+ $curl = curl_init($url . ($method == 'GET' && $params ? '?' . $params : ''));
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt($curl, CURLOPT_HEADER, false);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLOPT_HTTPHEADER, array('Accept: application/xrds+xml, */*'));
+
+ if($this->verify_peer !== null) {
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verify_peer);
+ if($this->capath) {
+ curl_setopt($curl, CURLOPT_CAPATH, $this->capath);
+ }
+
+ if($this->cainfo) {
+ curl_setopt($curl, CURLOPT_CAINFO, $this->cainfo);
+ }
+ }
+
+ if ($method == 'POST') {
+ curl_setopt($curl, CURLOPT_POST, true);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
+ } elseif ($method == 'HEAD') {
+ curl_setopt($curl, CURLOPT_HEADER, true);
+ curl_setopt($curl, CURLOPT_NOBODY, true);
+ } else {
+ curl_setopt($curl, CURLOPT_HTTPGET, true);
+ }
+ $response = curl_exec($curl);
+
+ if($method == 'HEAD') {
+ $headers = array();
+ foreach(explode("\n", $response) as $header) {
+ $pos = strpos($header,':');
+ $name = strtolower(trim(substr($header, 0, $pos)));
+ $headers[$name] = trim(substr($header, $pos+1));
+ }
+
+ # Updating claimed_id in case of redirections.
+ $effective_url = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL);
+ if($effective_url != $url) {
+ $this->identity = $this->claimed_id = $effective_url;
+ }
+
+ return $headers;
+ }
+
+ if (curl_errno($curl)) {
+ throw new ErrorException(curl_error($curl), curl_errno($curl));
+ }
+
+ return $response;
+ }
+
+ protected function request_streams($url, $method='GET', $params=array())
+ {
+ if(!$this->hostExists($url)) {
+ throw new ErrorException("Could not connect to $url.", 404);
+ }
+
+ $params = http_build_query($params, '', '&');
+ switch($method) {
+ case 'GET':
+ $opts = array(
+ 'http' => array(
+ 'method' => 'GET',
+ 'header' => 'Accept: application/xrds+xml, */*',
+ 'ignore_errors' => true,
+ ), 'ssl' => array(
+ 'CN_match' => parse_url($url, PHP_URL_HOST),
+ ),
+ );
+ $url = $url . ($params ? '?' . $params : '');
+ break;
+ case 'POST':
+ $opts = array(
+ 'http' => array(
+ 'method' => 'POST',
+ 'header' => 'Content-type: application/x-www-form-urlencoded',
+ 'content' => $params,
+ 'ignore_errors' => true,
+ ), 'ssl' => array(
+ 'CN_match' => parse_url($url, PHP_URL_HOST),
+ ),
+ );
+ break;
+ case 'HEAD':
+ # We want to send a HEAD request,
+ # but since get_headers doesn't accept $context parameter,
+ # we have to change the defaults.
+ $default = stream_context_get_options(stream_context_get_default());
+ stream_context_get_default(
+ array(
+ 'http' => array(
+ 'method' => 'HEAD',
+ 'header' => 'Accept: application/xrds+xml, */*',
+ 'ignore_errors' => true,
+ ), 'ssl' => array(
+ 'CN_match' => parse_url($url, PHP_URL_HOST),
+ ),
+ )
+ );
+
+ $url = $url . ($params ? '?' . $params : '');
+ $headers_tmp = get_headers ($url);
+ if(!$headers_tmp) {
+ return array();
+ }
+
+ # Parsing headers.
+ $headers = array();
+ foreach($headers_tmp as $header) {
+ $pos = strpos($header,':');
+ $name = strtolower(trim(substr($header, 0, $pos)));
+ $headers[$name] = trim(substr($header, $pos+1));
+
+ # Following possible redirections. The point is just to have
+ # claimed_id change with them, because get_headers() will
+ # follow redirections automatically.
+ # We ignore redirections with relative paths.
+ # If any known provider uses them, file a bug report.
+ if($name == 'location') {
+ if(strpos($headers[$name], 'http') === 0) {
+ $this->identity = $this->claimed_id = $headers[$name];
+ } elseif($headers[$name][0] == '/') {
+ $parsed_url = parse_url($this->claimed_id);
+ $this->identity =
+ $this->claimed_id = $parsed_url['scheme'] . '://'
+ . $parsed_url['host']
+ . $headers[$name];
+ }
+ }
+ }
+
+ # And restore them.
+ stream_context_get_default($default);
+ return $headers;
+ }
+
+ if($this->verify_peer) {
+ $opts['ssl'] += array(
+ 'verify_peer' => true,
+ 'capath' => $this->capath,
+ 'cafile' => $this->cainfo,
+ );
+ }
+
+ $context = stream_context_create ($opts);
+
+ return file_get_contents($url, false, $context);
+ }
+
+ protected function request($url, $method='GET', $params=array())
+ {
+ if (function_exists('curl_init')
+ && (!in_array('https', stream_get_wrappers()) || !ini_get('safe_mode') && !ini_get('open_basedir'))
+ ) {
+ return $this->request_curl($url, $method, $params);
+ }
+ return $this->request_streams($url, $method, $params);
+ }
+
+ protected function build_url($url, $parts)
+ {
+ if (isset($url['query'], $parts['query'])) {
+ $parts['query'] = $url['query'] . '&' . $parts['query'];
+ }
+
+ $url = $parts + $url;
+ $url = $url['scheme'] . '://'
+ . (empty($url['username'])?''
+ :(empty($url['password'])? "{$url['username']}@"
+ :"{$url['username']}:{$url['password']}@"))
+ . $url['host']
+ . (empty($url['port'])?'':":{$url['port']}")
+ . (empty($url['path'])?'':$url['path'])
+ . (empty($url['query'])?'':"?{$url['query']}")
+ . (empty($url['fragment'])?'':"#{$url['fragment']}");
+ return $url;
+ }
+
+ /**
+ * Helper function used to scan for <meta>/<link> tags and extract information
+ * from them
+ */
+ protected function htmlTag($content, $tag, $attrName, $attrValue, $valueName)
+ {
+ preg_match_all("#<{$tag}[^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*$valueName=['\"](.+?)['\"][^>]*/?>#i", $content, $matches1);
+ preg_match_all("#<{$tag}[^>]*$valueName=['\"](.+?)['\"][^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*/?>#i", $content, $matches2);
+
+ $result = array_merge($matches1[1], $matches2[1]);
+ return empty($result)?false:$result[0];
+ }
+
+ /**
+ * Performs Yadis and HTML discovery. Normally not used.
+ * @param $url Identity URL.
+ * @return String OP Endpoint (i.e. OpenID provider address).
+ * @throws ErrorException
+ */
+ function discover($url)
+ {
+ if (!$url) throw new ErrorException('No identity supplied.');
+ # Use xri.net proxy to resolve i-name identities
+ if (!preg_match('#^https?:#', $url)) {
+ $url = "https://xri.net/$url";
+ }
+
+ # We save the original url in case of Yadis discovery failure.
+ # It can happen when we'll be lead to an XRDS document
+ # which does not have any OpenID2 services.
+ $originalUrl = $url;
+
+ # A flag to disable yadis discovery in case of failure in headers.
+ $yadis = true;
+
+ # We'll jump a maximum of 5 times, to avoid endless redirections.
+ for ($i = 0; $i < 5; $i ++) {
+ if ($yadis) {
+ $headers = $this->request($url, 'HEAD');
+
+ $next = false;
+ if (isset($headers['x-xrds-location'])) {
+ $url = $this->build_url(parse_url($url), parse_url(trim($headers['x-xrds-location'])));
+ $next = true;
+ }
+
+ if (isset($headers['content-type'])
+ && (strpos($headers['content-type'], 'application/xrds+xml') !== false
+ || strpos($headers['content-type'], 'text/xml') !== false)
+ ) {
+ # Apparently, some providers return XRDS documents as text/html.
+ # While it is against the spec, allowing this here shouldn't break
+ # compatibility with anything.
+ # ---
+ # Found an XRDS document, now let's find the server, and optionally delegate.
+ $content = $this->request($url, 'GET');
+
+ preg_match_all('#<Service.*?>(.*?)</Service>#s', $content, $m);
+ foreach($m[1] as $content) {
+ $content = ' ' . $content; # The space is added, so that strpos doesn't return 0.
+
+ # OpenID 2
+ $ns = preg_quote('http://specs.openid.net/auth/2.0/');
+ if(preg_match('#<Type>\s*'.$ns.'(server|signon)\s*</Type>#s', $content, $type)) {
+ if ($type[1] == 'server') $this->identifier_select = true;
+
+ preg_match('#<URI.*?>(.*)</URI>#', $content, $server);
+ preg_match('#<(Local|Canonical)ID>(.*)</\1ID>#', $content, $delegate);
+ if (empty($server)) {
+ return false;
+ }
+ # Does the server advertise support for either AX or SREG?
+ $this->ax = (bool) strpos($content, '<Type>http://openid.net/srv/ax/1.0</Type>');
+ $this->sreg = strpos($content, '<Type>http://openid.net/sreg/1.0</Type>')
+ || strpos($content, '<Type>http://openid.net/extensions/sreg/1.1</Type>');
+
+ $server = $server[1];
+ if (isset($delegate[2])) $this->identity = trim($delegate[2]);
+ $this->version = 2;
+
+ $this->server = $server;
+ return $server;
+ }
+
+ # OpenID 1.1
+ $ns = preg_quote('http://openid.net/signon/1.1');
+ if (preg_match('#<Type>\s*'.$ns.'\s*</Type>#s', $content)) {
+
+ preg_match('#<URI.*?>(.*)</URI>#', $content, $server);
+ preg_match('#<.*?Delegate>(.*)</.*?Delegate>#', $content, $delegate);
+ if (empty($server)) {
+ return false;
+ }
+ # AX can be used only with OpenID 2.0, so checking only SREG
+ $this->sreg = strpos($content, '<Type>http://openid.net/sreg/1.0</Type>')
+ || strpos($content, '<Type>http://openid.net/extensions/sreg/1.1</Type>');
+
+ $server = $server[1];
+ if (isset($delegate[1])) $this->identity = $delegate[1];
+ $this->version = 1;
+
+ $this->server = $server;
+ return $server;
+ }
+ }
+
+ $next = true;
+ $yadis = false;
+ $url = $originalUrl;
+ $content = null;
+ break;
+ }
+ if ($next) continue;
+
+ # There are no relevant information in headers, so we search the body.
+ $content = $this->request($url, 'GET');
+ $location = $this->htmlTag($content, 'meta', 'http-equiv', 'X-XRDS-Location', 'content');
+ if ($location) {
+ $url = $this->build_url(parse_url($url), parse_url($location));
+ continue;
+ }
+ }
+
+ if (!$content) $content = $this->request($url, 'GET');
+
+ # At this point, the YADIS Discovery has failed, so we'll switch
+ # to openid2 HTML discovery, then fallback to openid 1.1 discovery.
+ $server = $this->htmlTag($content, 'link', 'rel', 'openid2.provider', 'href');
+ $delegate = $this->htmlTag($content, 'link', 'rel', 'openid2.local_id', 'href');
+ $this->version = 2;
+
+ if (!$server) {
+ # The same with openid 1.1
+ $server = $this->htmlTag($content, 'link', 'rel', 'openid.server', 'href');
+ $delegate = $this->htmlTag($content, 'link', 'rel', 'openid.delegate', 'href');
+ $this->version = 1;
+ }
+
+ if ($server) {
+ # We found an OpenID2 OP Endpoint
+ if ($delegate) {
+ # We have also found an OP-Local ID.
+ $this->identity = $delegate;
+ }
+ $this->server = $server;
+ return $server;
+ }
+
+ throw new ErrorException("No OpenID Server found at $url", 404);
+ }
+ throw new ErrorException('Endless redirection!', 500);
+ }
+
+ protected function sregParams()
+ {
+ $params = array();
+ # We always use SREG 1.1, even if the server is advertising only support for 1.0.
+ # That's because it's fully backwards compatibile with 1.0, and some providers
+ # advertise 1.0 even if they accept only 1.1. One such provider is myopenid.com
+ $params['openid.ns.sreg'] = 'http://openid.net/extensions/sreg/1.1';
+ if ($this->required) {
+ $params['openid.sreg.required'] = array();
+ foreach ($this->required as $required) {
+ if (!isset(self::$ax_to_sreg[$required])) continue;
+ $params['openid.sreg.required'][] = self::$ax_to_sreg[$required];
+ }
+ $params['openid.sreg.required'] = implode(',', $params['openid.sreg.required']);
+ }
+
+ if ($this->optional) {
+ $params['openid.sreg.optional'] = array();
+ foreach ($this->optional as $optional) {
+ if (!isset(self::$ax_to_sreg[$optional])) continue;
+ $params['openid.sreg.optional'][] = self::$ax_to_sreg[$optional];
+ }
+ $params['openid.sreg.optional'] = implode(',', $params['openid.sreg.optional']);
+ }
+ return $params;
+ }
+
+ protected function axParams()
+ {
+ $params = array();
+ if ($this->required || $this->optional) {
+ $params['openid.ns.ax'] = 'http://openid.net/srv/ax/1.0';
+ $params['openid.ax.mode'] = 'fetch_request';
+ $this->aliases = array();
+ $counts = array();
+ $required = array();
+ $optional = array();
+ foreach (array('required','optional') as $type) {
+ foreach ($this->$type as $alias => $field) {
+ if (is_int($alias)) $alias = strtr($field, '/', '_');
+ $this->aliases[$alias] = 'http://axschema.org/' . $field;
+ if (empty($counts[$alias])) $counts[$alias] = 0;
+ $counts[$alias] += 1;
+ ${$type}[] = $alias;
+ }
+ }
+ foreach ($this->aliases as $alias => $ns) {
+ $params['openid.ax.type.' . $alias] = $ns;
+ }
+ foreach ($counts as $alias => $count) {
+ if ($count == 1) continue;
+ $params['openid.ax.count.' . $alias] = $count;
+ }
+
+ # Don't send empty ax.requied and ax.if_available.
+ # Google and possibly other providers refuse to support ax when one of these is empty.
+ if($required) {
+ $params['openid.ax.required'] = implode(',', $required);
+ }
+ if($optional) {
+ $params['openid.ax.if_available'] = implode(',', $optional);
+ }
+ }
+ return $params;
+ }
+
+ protected function authUrl_v1($immediate)
+ {
+ $returnUrl = $this->returnUrl;
+ # If we have an openid.delegate that is different from our claimed id,
+ # we need to somehow preserve the claimed id between requests.
+ # The simplest way is to just send it along with the return_to url.
+ if($this->identity != $this->claimed_id) {
+ $returnUrl .= (strpos($returnUrl, '?') ? '&' : '?') . 'openid.claimed_id=' . $this->claimed_id;
+ }
+
+ $params = array(
+ 'openid.return_to' => $returnUrl,
+ 'openid.mode' => $immediate ? 'checkid_immediate' : 'checkid_setup',
+ 'openid.identity' => $this->identity,
+ 'openid.trust_root' => $this->trustRoot,
+ ) + $this->sregParams();
+
+ return $this->build_url(parse_url($this->server)
+ , array('query' => http_build_query($params, '', '&')));
+ }
+
+ protected function authUrl_v2($immediate)
+ {
+ $params = array(
+ 'openid.ns' => 'http://specs.openid.net/auth/2.0',
+ 'openid.mode' => $immediate ? 'checkid_immediate' : 'checkid_setup',
+ 'openid.return_to' => $this->returnUrl,
+ 'openid.realm' => $this->trustRoot,
+ );
+ if ($this->ax) {
+ $params += $this->axParams();
+ }
+ if ($this->sreg) {
+ $params += $this->sregParams();
+ }
+ if (!$this->ax && !$this->sreg) {
+ # If OP doesn't advertise either SREG, nor AX, let's send them both
+ # in worst case we don't get anything in return.
+ $params += $this->axParams() + $this->sregParams();
+ }
+
+ if ($this->identifier_select) {
+ $params['openid.identity'] = $params['openid.claimed_id']
+ = 'http://specs.openid.net/auth/2.0/identifier_select';
+ } else {
+ $params['openid.identity'] = $this->identity;
+ $params['openid.claimed_id'] = $this->claimed_id;
+ }
+
+ return $this->build_url(parse_url($this->server)
+ , array('query' => http_build_query($params, '', '&')));
+ }
+
+ /**
+ * Returns authentication url. Usually, you want to redirect your user to it.
+ * @return String The authentication url.
+ * @param String $select_identifier Whether to request OP to select identity for an user in OpenID 2. Does not affect OpenID 1.
+ * @throws ErrorException
+ */
+ function authUrl($immediate = false)
+ {
+ if ($this->setup_url && !$immediate) return $this->setup_url;
+ if (!$this->server) $this->discover($this->identity);
+
+ if ($this->version == 2) {
+ return $this->authUrl_v2($immediate);
+ }
+ return $this->authUrl_v1($immediate);
+ }
+
+ /**
+ * Performs OpenID verification with the OP.
+ * @return Bool Whether the verification was successful.
+ * @throws ErrorException
+ */
+ function validate()
+ {
+ # If the request was using immediate mode, a failure may be reported
+ # by presenting user_setup_url (for 1.1) or reporting
+ # mode 'setup_needed' (for 2.0). Also catching all modes other than
+ # id_res, in order to avoid throwing errors.
+ if(isset($this->data['openid_user_setup_url'])) {
+ $this->setup_url = $this->data['openid_user_setup_url'];
+ return false;
+ }
+ if($this->mode != 'id_res') {
+ return false;
+ }
+
+ $this->claimed_id = isset($this->data['openid_claimed_id'])?$this->data['openid_claimed_id']:$this->data['openid_identity'];
+ $params = array(
+ 'openid.assoc_handle' => $this->data['openid_assoc_handle'],
+ 'openid.signed' => $this->data['openid_signed'],
+ 'openid.sig' => $this->data['openid_sig'],
+ );
+
+ if (isset($this->data['openid_ns'])) {
+ # We're dealing with an OpenID 2.0 server, so let's set an ns
+ # Even though we should know location of the endpoint,
+ # we still need to verify it by discovery, so $server is not set here
+ $params['openid.ns'] = 'http://specs.openid.net/auth/2.0';
+ } elseif (isset($this->data['openid_claimed_id'])
+ && $this->data['openid_claimed_id'] != $this->data['openid_identity']
+ ) {
+ # If it's an OpenID 1 provider, and we've got claimed_id,
+ # we have to append it to the returnUrl, like authUrl_v1 does.
+ $this->returnUrl .= (strpos($this->returnUrl, '?') ? '&' : '?')
+ . 'openid.claimed_id=' . $this->claimed_id;
+ }
+
+ if ($this->data['openid_return_to'] != $this->returnUrl) {
+ # The return_to url must match the url of current request.
+ # I'm assuing that noone will set the returnUrl to something that doesn't make sense.
+ return false;
+ }
+
+ $server = $this->discover($this->claimed_id);
+
+ foreach (explode(',', $this->data['openid_signed']) as $item) {
+ # Checking whether magic_quotes_gpc is turned on, because
+ # the function may fail if it is. For example, when fetching
+ # AX namePerson, it might containg an apostrophe, which will be escaped.
+ # In such case, validation would fail, since we'd send different data than OP
+ # wants to verify. stripslashes() should solve that problem, but we can't
+ # use it when magic_quotes is off.
+ $value = $this->data['openid_' . str_replace('.','_',$item)];
+ $params['openid.' . $item] = get_magic_quotes_gpc() ? stripslashes($value) : $value;
+
+ }
+
+ $params['openid.mode'] = 'check_authentication';
+
+ $response = $this->request($server, 'POST', $params);
+
+ return preg_match('/is_valid\s*:\s*true/i', $response);
+ }
+
+ protected function getAxAttributes()
+ {
+ $alias = null;
+ if (isset($this->data['openid_ns_ax'])
+ && $this->data['openid_ns_ax'] != 'http://openid.net/srv/ax/1.0'
+ ) { # It's the most likely case, so we'll check it before
+ $alias = 'ax';
+ } else {
+ # 'ax' prefix is either undefined, or points to another extension,
+ # so we search for another prefix
+ foreach ($this->data as $key => $val) {
+ if (substr($key, 0, strlen('openid_ns_')) == 'openid_ns_'
+ && $val == 'http://openid.net/srv/ax/1.0'
+ ) {
+ $alias = substr($key, strlen('openid_ns_'));
+ break;
+ }
+ }
+ }
+ if (!$alias) {
+ # An alias for AX schema has not been found,
+ # so there is no AX data in the OP's response
+ return array();
+ }
+
+ $attributes = array();
+ foreach (explode(',', $this->data['openid_signed']) as $key) {
+ $keyMatch = $alias . '.value.';
+ if (substr($key, 0, strlen($keyMatch)) != $keyMatch) {
+ continue;
+ }
+ $key = substr($key, strlen($keyMatch));
+ if (!isset($this->data['openid_' . $alias . '_type_' . $key])) {
+ # OP is breaking the spec by returning a field without
+ # associated ns. This shouldn't happen, but it's better
+ # to check, than cause an E_NOTICE.
+ continue;
+ }
+ $value = $this->data['openid_' . $alias . '_value_' . $key];
+ $key = substr($this->data['openid_' . $alias . '_type_' . $key],
+ strlen('http://axschema.org/'));
+
+ $attributes[$key] = $value;
+ }
+ return $attributes;
+ }
+
+ protected function getSregAttributes()
+ {
+ $attributes = array();
+ $sreg_to_ax = array_flip(self::$ax_to_sreg);
+ foreach (explode(',', $this->data['openid_signed']) as $key) {
+ $keyMatch = 'sreg.';
+ if (substr($key, 0, strlen($keyMatch)) != $keyMatch) {
+ continue;
+ }
+ $key = substr($key, strlen($keyMatch));
+ if (!isset($sreg_to_ax[$key])) {
+ # The field name isn't part of the SREG spec, so we ignore it.
+ continue;
+ }
+ $attributes[$sreg_to_ax[$key]] = $this->data['openid_sreg_' . $key];
+ }
+ return $attributes;
+ }
+
+ /**
+ * Gets AX/SREG attributes provided by OP. should be used only after successful validaton.
+ * Note that it does not guarantee that any of the required/optional parameters will be present,
+ * or that there will be no other attributes besides those specified.
+ * In other words. OP may provide whatever information it wants to.
+ * * SREG names will be mapped to AX names.
+ * * @return Array Array of attributes with keys being the AX schema names, e.g. 'contact/email'
+ * @see http://www.axschema.org/types/
+ */
+ function getAttributes()
+ {
+ if (isset($this->data['openid_ns'])
+ && $this->data['openid_ns'] == 'http://specs.openid.net/auth/2.0'
+ ) { # OpenID 2.0
+ # We search for both AX and SREG attributes, with AX taking precedence.
+ return $this->getAxAttributes() + $this->getSregAttributes();
+ }
+ return $this->getSregAttributes();
+ }
+}
+
--- a/lib/staticmaplite/.gitignore
+++ /dev/null
@@ -1,4 +1,1 @@
-cache/tiles
-cache/map
-cache/maps
--- a/lib/staticmaplite/images/markers/GPlotter - Make Google Maps Easily.URL
+++ /dev/null
@@ -1,3 +1,1 @@
-[InternetShortcut]
-URL=http://gplotter.offwhite.net/
--- a/lib/staticmaplite/images/markers/Google Maps Icons, Free!.URL
+++ /dev/null
@@ -1,3 +1,1 @@
-[InternetShortcut]
-URL=http://brennan.offwhite.net/blog/2005/07/23/new-google-maps-icons-free/
Binary files a/lib/staticmaplite/images/markers/Thumbs.db and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb1.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb10.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb11.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb12.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb13.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb14.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb15.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb16.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb17.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb18.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb19.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb2.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb20.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb21.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb22.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb23.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb24.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb25.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb3.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb4.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb5.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb6.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb7.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb8.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconb9.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong1.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong10.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong11.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong12.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong13.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong14.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong15.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong16.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong17.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong18.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong19.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong2.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong20.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong21.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong22.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong23.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong24.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong25.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong3.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong4.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong5.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong6.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong7.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong8.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icong9.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr1.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr10.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr11.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr12.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr13.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr14.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr15.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr16.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr17.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr18.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr19.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr2.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr20.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr21.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr22.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr23.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr24.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr25.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr3.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr4.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr5.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr6.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr7.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr8.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/iconr9.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/icons.psd and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/lightblue1.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/lightblue2.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/lightblue3.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/lightblue4.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/lightblue5.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/ol-marker-blue.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/ol-marker-gold.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/ol-marker-green.png and /dev/null differ
Binary files a/lib/staticmaplite/images/markers/ol-marker.png and /dev/null differ
Binary files a/lib/staticmaplite/images/osm_logo.png and /dev/null differ
--- a/lib/staticmaplite/index.html
+++ /dev/null
@@ -1,122 +1,1 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de-de" lang="de-de">
-<head>
- <!--
- CSS based on template of Dandelion wiki engine by Radomir Dopieralski who released this
- template under the terms of GNU GPL. http://dandelion.sheep.art.pl/
- -->
- <meta http-equiv="content-type" content="text/html; charset=utf-8" />
- <title>staticMapLite</title>
- <style type="text/css">
-html{font:96% sans-serif;color:#000;background:#f7f7f7;line-height:1.4;}
-body{color:#333;}
-#wrapper{margin:auto;width:60em;position:relative;}
-#header{padding:0px 0px 7px 0px; height: 1em;}
-#header h1 { float:left; width: 40%; }
-#content{background:white;padding:1em;border:1px solid #e0d78a;outline:0.5em solid #fef4a4; margin:0.5em 0;padding:20px;min-height:20em;}
-h1{margin-top:0px;}
-h1,h2,h3,h4,h5,h6{letter-spacing:0.05em;color:#1474CA;font-weight:normal;}
-h1 a:hover,h2 a:hover,h3 a:hover,h4 a:hover,h5 a:hover,h6 a:hover{text-decoration:none;}
-a{color:#1474CA;text-decoration:none;}
-a:visited{color:#1474CA;}
-a.pending{color:#c174a0;}
-a:hover{text-decoration:underline;}
-a img{border:none;}
-input,textarea{font-size:94%;border:1px solid #999;background:#fff;color:#666;outline:0.2em solid #eee;padding:0px;line-height:1.2;margin:0.5em;vertical-align:middle;}
-textarea{display:block;margin:0.5em auto;width:100%;}
-pre{outline:0.4em solid #eee;padding:0.5em;margin:0.5em;border:1px solid #e0d78a;background:#fef4a4;color:#644e22;}
-img{border:1px solid #ccc;outline:0.25em solid #eee;padding:0.25em;margin:0.25em 0 0.25em 0.5em;background:#fff;}
-hr{height:0;border:none;color:#fff;background:transparent;border-bottom:1px solid #ccc; margin:0.5em 0;}
-#diff {outline:none;border:none;}
-#diff ins{color:green;text-decoration:none;font-weight:bold;}
-#diff del{color:red;text-decoration:line-through;}
-#diff{background:#fff;line-height:1.25;padding:1em;white-space:pre-wrap;word-wrap:break-word; white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;width:97%;}
-hr{margin:10px 0 10px 0;height:0px;overflow:hidden;border:0px;border-top:2px solid #ccc;}
-.error{color:#F25A5A;font-weight:bold;}
-form{display:inline;}
-#contentTextarea{height:44em;}
-#toc{margin:5px 0 5px 10px;padding:6px 5px 7px 0px;float:right;list-style:none;outline:0.4em solid #eee;background:#fef4a4;border:1px solid #e0d78a;}
-#toc ul{list-style:none;padding:3px 0 3px 10px;}
-#toc li{font-size:11px;padding-left:10px;}
-#toc ul li{font-size:10px;}
-#toc ul ul li{font-size:9px;}
-#toc ul ul ul li{font-size:8px;}
-#toc ul ul ul ul li{font-size:7px;}
-.pageVersionsList{letter-spacing:0px;font-variant:normal;font-size:12px;}
-#renameForm{float:left;}
-.clear{clear:both;}
-.tagList{padding:0.2em 0.4em 0.2em 0.4em;margin-top:0.5em;border:1px dashed #e0d78a;background:#fef4a4;color:#644e22;}
-.tagCloud{float:right;width:200px;padding:0.5em;margin:1em;border:1px dashed #e0d78a;background:#fef4a4;color:#644e22;}
-#fileTable{border-collapse:collapse;}
-#fileTable td{border:1px solid #FEF4A4;padding:2px 6px 2px 6px;}
-h2 span.par-edit, h3 span.par-edit, h4 span.par-edit, h5 span.par-edit, h6 span.par-edit {display:none;}
-h2:hover span.par-edit, h3:hover span.par-edit, h4:hover span.par-edit, h5:hover span.par-edit, h6:hover span.par-edit {display:inline;font-size:x-small;}
-.comment-item { border:1px solid #999;color:#666;outline:0.2em solid #eee; }
-.resizeTextareaDiv { margin-top: 5px;}
-a.toolbarTextareaItem { padding-right: 10px; }
-a.external:after { content: "\2197";}
- </style>
- </head>
-<body>
- <div id="wrapper">
- <div id="header">
- </div>
- <div id="content">
-
- <div class="par-div">
- <h2>
- staticMapLite - simple map for your website
- </h2>
- <p>
- <img src="staticmap.php?center=40.714728,-73.998672&zoom=14&size=865x512&maptype=mapnik" width="865" height="512" /></p>
- <p>
- This image was created using the following simple <img> tag:
-<pre><img src="staticmap.php?center=40.714728,-73.998672&zoom=14&size=865x512&maptype=mapnik" /></pre>
- </p>
- </div>
- <hr />
- <div class="par-div">
- <h3>
- Place Markers
- </h3>
-
- <p>
- <img src="staticmap.php?center=40.714728,-73.998672&zoom=14&size=865x512&maptype=mapnik&markers=40.702147,-74.015794,lightblue1|40.711614,-74.012318,lightblue2|40.718217,-73.998284,lightblue3" width="865" height="512" />
-</p><p> Add markers by appending them to the image URL:
-<pre>markers=40.702147,-74.015794,lightblue1|40.711614,-74.012318,lightblue2|40.718217,-73.998284,lightblue3</pre>
- </p>
- </div>
- <hr />
- <div class="par-div">
- <h3>
- Use Different Map Styles (Tile Sources)
- </h3>
-
- <p>
- <div style="float:left; margin-right: 10px">
- <img src="staticmap.php?center=40.714728,-73.998672&zoom=14&size=256x256&maptype=mapnik" width="256" height="256" />
- <pre>maptype=mapnik</pre>
- </div>
- <div style="float:left; margin-right: 10px">
- <img src="staticmap.php?center=40.714728,-73.998672&zoom=14&size=256x256&maptype=osmarenderer" width="256" height="256" />
- <pre>maptype=osmarenderer</pre>
- </div>
- <div style="float:left; margin-right: 10px">
- <img src="staticmap.php?center=40.714728,-73.998672&zoom=14&size=256x256&maptype=cycle" width="256" height="256" />
- <pre>maptype=cycle</pre>
- </div>
- <br style="clear:both" />
- </p>
- </div>
-
- </div>
- <div id="footer">
- <div style="text-align:center;padding:7px;color:#ccc">
- sponsored by <a href="http://dfacts.de">dFacts Network</a>
- </div>
- </div>
-</div>
-</body>
-</html>
--- a/lib/staticmaplite/selinux-fix.sh
+++ /dev/null
@@ -1,3 +1,1 @@
-chcon -R -t httpd_sys_content_rw_t cache
-
--- a/lib/staticmaplite/staticmap.php
+++ /dev/null
@@ -1,273 +1,1 @@
-<?php
-/**
- * staticMapLite 0.02
- *
- * Copyright 2009 Gerhard Koch
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @author Gerhard Koch <gerhard.koch AT ymail.com>
- *
- * USAGE:
- *
- * staticmap.php?center=40.714728,-73.998672&zoom=14&size=512x512&maptype=mapnik&markers=40.702147,-74.015794,blues|40.711614,-74.012318,greeng|40.718217,-73.998284,redc
- *
- */
-
-error_reporting(0);
-ini_set('display_errors','off');
-
-Class staticMapLite {
-
- protected $tileSize = 256;
- protected $tileSrcUrl = array( 'mapnik' => 'http://tile.openstreetmap.org/{Z}/{X}/{Y}.png',
- 'cloudmade' => 'http://b.tile.cloudmade.com/daa03470bb8740298d4b10e3f03d63e6/1/256/{Z}/{X}/{Y}.png',);
-
- protected $tileDefaultSrc = 'cloudmade';
- protected $markerBaseDir = 'images/markers';
- protected $osmLogo = 'images/osm_logo.png';
-
- protected $useTileCache = true;
- protected $tileCacheBaseDir = './cache/tiles';
-
- protected $useMapCache = true;
- protected $mapCacheBaseDir = './cache/maps';
- protected $mapCacheID = '';
- protected $mapCacheFile = '';
- protected $mapCacheExtension = 'png';
-
- protected $zoom, $lat, $lon, $width, $height, $markers, $image, $maptype;
- protected $centerX, $centerY, $offsetX, $offsetY;
-
- public function __construct(){
- $this->zoom = 0;
- $this->lat = 0;
- $this->lon = 0;
- $this->width = 500;
- $this->height = 350;
- $this->markers = array();
- $this->maptype = $this->tileDefaultSrc;
- }
-
- public function parseParams(){
- global $_GET;
-
- // get zoom from GET paramter
- $this->zoom = $_GET['zoom']?intval($_GET['zoom']):0;
- if($this->zoom>18)$this->zoom = 18;
-
- // get lat and lon from GET paramter
- list($this->lat,$this->lon) = split(',',$_GET['center']);
- $this->lat = floatval($this->lat);
- $this->lon = floatval($this->lon);
-
- // get zoom from GET paramter
- if($_GET['size']){
- list($this->width, $this->height) = split('x',$_GET['size']);
- $this->width = intval($this->width);
- $this->height = intval($this->height);
- }
- if($_GET['markers']){
- $markers = split('%7C|\|',$_GET['markers']);
- foreach($markers as $marker){
- list($markerLat, $markerLon, $markerImage) = split(',',$marker);
- $markerLat = floatval($markerLat);
- $markerLon = floatval($markerLon);
- $markerImage = basename($markerImage);
- $this->markers[] = array('lat'=>$markerLat, 'lon'=>$markerLon, 'image'=>$markerImage);
- }
-
- }
- if($_GET['maptype']){
- if(array_key_exists($_GET['maptype'],$this->tileSrcUrl)) $this->maptype = $_GET['maptype'];
- }
- }
-
- public function lonToTile($long, $zoom){
- return (($long + 180) / 360) * pow(2, $zoom);
- }
-
- public function latToTile($lat, $zoom){
- return (1 - log(tan($lat * pi()/180) + 1 / cos($lat* pi()/180)) / pi()) /2 * pow(2, $zoom);
- }
-
- public function initCoords(){
- $this->centerX = $this->lonToTile($this->lon, $this->zoom);
- $this->centerY = $this->latToTile($this->lat, $this->zoom);
- $this->offsetX = floor((floor($this->centerX)-$this->centerX)*$this->tileSize);
- $this->offsetY = floor((floor($this->centerY)-$this->centerY)*$this->tileSize);
- }
-
- public function createBaseMap(){
- $this->image = imagecreatetruecolor($this->width, $this->height);
- $startX = floor($this->centerX-($this->width/$this->tileSize)/2);
- $startY = floor($this->centerY-($this->height/$this->tileSize)/2);
- $endX = ceil($this->centerX+($this->width/$this->tileSize)/2);
- $endY = ceil($this->centerY+($this->height/$this->tileSize)/2);
- $this->offsetX = -floor(($this->centerX-floor($this->centerX))*$this->tileSize);
- $this->offsetY = -floor(($this->centerY-floor($this->centerY))*$this->tileSize);
- $this->offsetX += floor($this->width/2);
- $this->offsetY += floor($this->height/2);
- $this->offsetX += floor($startX-floor($this->centerX))*$this->tileSize;
- $this->offsetY += floor($startY-floor($this->centerY))*$this->tileSize;
-
- for($x=$startX; $x<=$endX; $x++){
- for($y=$startY; $y<=$endY; $y++){
- $url = str_replace(array('{Z}','{X}','{Y}'),array($this->zoom, $x, $y), $this->tileSrcUrl[$this->maptype]);
- $tileImage = imagecreatefromstring($this->fetchTile($url));
- $destX = ($x-$startX)*$this->tileSize+$this->offsetX;
- $destY = ($y-$startY)*$this->tileSize+$this->offsetY;
- imagecopy($this->image, $tileImage, $destX, $destY, 0, 0, $this->tileSize, $this->tileSize);
- }
- }
- }
-
-
- public function placeMarkers(){
- foreach($this->markers as $marker){
- $markerLat = $marker['lat'];
- $markerLon = $marker['lon'];
- $markerImage = $marker['image'];
- $markerIndex++;
- $markerFilename = $markerImage?(file_exists($this->markerBaseDir.'/'.$markerImage.".png")?$markerImage:'lightblue'.$markerIndex):'lightblue'.$markerIndex;
- if(file_exists($this->markerBaseDir.'/'.$markerFilename.".png")){
- $markerImg = imagecreatefrompng($this->markerBaseDir.'/'.$markerFilename.".png");
- } else {
- $markerImg = imagecreatefrompng($this->markerBaseDir.'/lightblue1.png');
- }
- $destX = floor(($this->width/2)-$this->tileSize*($this->centerX-$this->lonToTile($markerLon, $this->zoom)));
- $destY = floor(($this->height/2)-$this->tileSize*($this->centerY-$this->latToTile($markerLat, $this->zoom)));
- $destY = $destY - imagesy($markerImg);
-
- imagecopy($this->image, $markerImg, $destX, $destY, 0, 0, imagesx($markerImg), imagesy($markerImg));
-
- };
-}
-
-
-
- public function tileUrlToFilename($url){
- return $this->tileCacheBaseDir."/".str_replace(array('http://'),'',$url);
- }
-
- public function checkTileCache($url){
- $filename = $this->tileUrlToFilename($url);
- if(file_exists($filename)){
- return file_get_contents($filename);
- }
- }
-
- public function checkMapCache(){
- $this->mapCacheID = md5($this->serializeParams());
- $filename = $this->mapCacheIDToFilename();
- if(file_exists($filename)) return true;
- }
-
- public function serializeParams(){
- return join("&",array($this->zoom,$this->lat,$this->lon,$this->width,$this->height, serialize($this->markers),$this->maptype));
- }
-
- public function mapCacheIDToFilename(){
- if(!$this->mapCacheFile){
- $this->mapCacheFile = $this->mapCacheBaseDir."/".substr($this->mapCacheID,0,2)."/".substr($this->mapCacheID,2,2)."/".substr($this->mapCacheID,4);
- }
- return $this->mapCacheFile.".".$this->mapCacheExtension;
- }
-
-
-
- public function mkdir_recursive($pathname, $mode){
- return mkdir($pathname, $mode, true);
- }
- public function writeTileToCache($url, $data){
- $filename = $this->tileUrlToFilename($url);
- $this->mkdir_recursive(dirname($filename),0777);
- file_put_contents($filename, $data);
- }
-
- public function fetchTile($url){
- if($this->useTileCache && ($cached = $this->checkTileCache($url))) return $cached;
- $ch = curl_init();
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0");
- curl_setopt($ch, CURLOPT_URL, $url);
- $tile = curl_exec($ch);
- curl_close($ch);
- if($this->useTileCache){
- $this->writeTileToCache($url,$tile);
- }
- return $tile;
-
- }
-
- public function copyrightNotice(){
- $logoImg = imagecreatefrompng($this->osmLogo);
- imagecopy($this->image, $logoImg, imagesx($this->image)-imagesx($logoImg), imagesy($this->image)-imagesy($logoImg), 0, 0, imagesx($logoImg), imagesy($logoImg));
-
- }
-
- public function sendHeader(){
- header('Content-Type: image/png');
- $expires = 60*60*24*14;
- header("Pragma: public");
- header("Cache-Control: maxage=".$expires);
- header('Expires: ' . gmdate('D, d M Y H:i:s', time()+$expires) . ' GMT');
- }
-
- public function makeMap(){
- $this->initCoords();
- $this->createBaseMap();
- if(count($this->markers))$this->placeMarkers();
- if($this->osmLogo) $this->copyrightNotice();
- }
-
- public function showMap(){
- $this->parseParams();
- if($this->useMapCache){
- // use map cache, so check cache for map
- if(!$this->checkMapCache()){
- // map is not in cache, needs to be build
- $this->makeMap();
- $this->mkdir_recursive(dirname($this->mapCacheIDToFilename()),0777);
- imagepng($this->image,$this->mapCacheIDToFilename(),9);
- $this->sendHeader();
- if(file_exists($this->mapCacheIDToFilename())){
- return file_get_contents($this->mapCacheIDToFilename());
- } else {
- return imagepng($this->image);
- }
- } else {
- // map is in cache
- $this->sendHeader();
- return file_get_contents($this->mapCacheIDToFilename());
- }
-
- } else {
- // no cache, make map, send headers and deliver png
- $this->makeMap();
- // $this->sendHeader();
- // do some extra compression
- imagetruecolortopalette($this->image, false, 256);
- return imagepng($this->image, 9, PNG_ALL_FILTERS);
-
- }
- }
-
-}
-
-$map = new staticMapLite();
-print $map->showMap();
-
-?>
-
--- a/myway/myway_api.json.php
+++ b/myway/myway_api.json.php
@@ -33,8 +33,8 @@
}
} */
//set POST variables
-$url = 'https://www.action.act.gov.au/ARTS/use_Funcs.asp';
-//$url = 'http://localhost/myway.htm';
+$url = 'https://www.transport.act.gov.au/ARTS/use_Funcs.asp';
+//$url = 'http://localhost/myway.html';
$field_mapping = Array(
"card_number" => "SRNO",
"DOBmonth" => "month",
@@ -81,7 +81,7 @@
curl_setopt($ch, CURLOPT_POST, count($fields));
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($ch, CURLOPT_REFERER, "https://www.action.act.gov.au/ARTS/getbalance.asp");
+ curl_setopt($ch, CURLOPT_REFERER, "https://www.transport.act.gov.au/ARTS/getbalance.asp");
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
//execute post
@@ -90,6 +90,39 @@
$return["error"][] = "Network error " . curl_errno($ch) . " " . curl_error($ch) . " " . $url . $fields_string;
//close connection
curl_close($ch);
+}
+
+function parseTable($table,$tableName) {
+ global $return;
+ $tableColumns = Array();
+ $tableColumnNum = 0;
+ foreach ($table->find("th") as $th) {
+ $tableColumns[$tableColumnNum] = cleanString($th->plaintext);
+ $tableColumnNum++;
+ }
+ //print_r($tableColumns);
+ $tableRowNum = 0;
+ foreach ($table->find("tr") as $tr) {
+ $tableColumnNum = 0;
+ foreach ($tr->find("td") as $td) {
+ if ($tableName == "myway_carddetails") {
+ // first table has card/cardholder details
+ $return[$tableName][$tableColumns[$tableColumnNum]] = cleanString($td->plaintext);
+ } else {
+ // second table has transactions
+
+ if ($tableColumns[$tableColumnNum] == "TX Reference No / Type") {
+ $return[$tableName][$tableRowNum]["TX Reference No"] = substr(cleanString($td->plaintext), 0, 6);
+ $return[$tableName][$tableRowNum]["TX Type"] = substr(cleanString($td->plaintext), 7);
+ } else {
+ $return[$tableName][$tableRowNum][$tableColumns[$tableColumnNum]] = cleanString($td->plaintext);
+ }
+ }
+ //print_r($return);
+ $tableColumnNum++;
+ }
+ $tableRowNum++;
+ }
}
if (!isset($return['error'])) {
@@ -101,43 +134,12 @@
$return['error'][] = $pageAlerts[0]->plaintext;
}
if (!isset($return['error'])) {
- $tableNum = 0;
- $tableName = Array(
- 1 => "myway_carddetails",
- 2 => "myway_transactions"
- );
- foreach ($page->find("table") as $table) {
- $tableNum++;
- $tableColumns = Array();
- $tableColumnNum = 0;
- foreach ($table->find("th") as $th) {
- $tableColumns[$tableColumnNum] = cleanString($th->plaintext);
- $tableColumnNum++;
- }
- //print_r($tableColumns);
- $tableRowNum = 0;
- foreach ($table->find("tr") as $tr) {
- $tableColumnNum = 0;
- foreach ($tr->find("td") as $td) {
- if ($tableNum == 1) {
- // first table has card/cardholder details
- $return[$tableName[$tableNum]][$tableColumns[$tableColumnNum]] = cleanString($td->plaintext);
- } else {
- // second table has transactions
-
- if ($tableColumns[$tableColumnNum] == "TX Reference No / Type") {
- $return[$tableName[$tableNum]][$tableRowNum]["TX Reference No"] = substr(cleanString($td->plaintext), 0, 6);
- $return[$tableName[$tableNum]][$tableRowNum]["TX Type"] = substr(cleanString($td->plaintext), 7);
- } else {
- $return[$tableName[$tableNum]][$tableRowNum][$tableColumns[$tableColumnNum]] = cleanString($td->plaintext);
- }
- }
- //print_r($return);
- $tableColumnNum++;
- }
- $tableRowNum++;
- }
- }
+ $tables = $page->find(".type3");
+ parseTable($tables[0], "myway_carddetails");
+
+ $tables = $page->find(".type2");
+ parseTable($tables[0], "myway_transactions");
+
}
}
if (sizeof($return) == 0) {
--- a/myway/myway_timeliness_reconcile.php
+++ b/myway/myway_timeliness_reconcile.php
@@ -80,7 +80,7 @@
echo "<h3>{$myway_stop[0]}</h3>";
$stopNameParts = explode(" ", $myway_stop[0]);
$markers = array();
- $stopKey = 1;
+ $stopKey = 0;
$foundStops = getStops(false, "", $stopNameParts[0] . " " . $stopNameParts[1]);
if (sizeof($foundStops) > 0) {
echo "<table>";
@@ -92,7 +92,7 @@
echo "<tr><td>" . $stopKey++ . "</td><td>" . $stopResult['stop_name'] . "</td><td>" . $stopResult['stop_code'] . "</td></tr>";
}
echo '</table>';
- echo "" . staticmap($markers, 0, "icong", false) . "<br>\n";
+ echo "" . staticmap($markers,false,false,false,true) . "<br>\n";
}
echo '<form id="inputform' . md5($myway_stop[0]) . '">
<input type="hidden" name="myway_stop" value="' . $myway_stop[0] . '">
--- /dev/null
+++ b/nbproject/project.properties
@@ -1,1 +1,8 @@
+include.path=${php.global.include.path}
+php.version=PHP_5
+source.encoding=UTF-8
+src.dir=.
+tags.asp=false
+tags.short=true
+web.root=.
--- /dev/null
+++ b/nbproject/project.xml
@@ -1,1 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+ <type>org.netbeans.modules.php.project</type>
+ <configuration>
+ <data xmlns="http://www.netbeans.org/ns/php-project/1">
+ <name>busui</name>
+ </data>
+ </configuration>
+</project>
--- a/stopList.php
+++ b/stopList.php
@@ -77,7 +77,7 @@
$sub_stop["stop_lon"]
);
}
- echo staticmap($stopPositions, 0, "iconb", true, true);
+ echo staticmap($stopPositions, true, true);
placeSettings();
echo '</span><span class="content-primary">';
} else if (isset($suburb)) {
--- a/trip.php
+++ b/trip.php
@@ -69,7 +69,7 @@
$tripStopTimes = getTimeInterpolatedTrip($tripid);
echo '<li data-role="list-divider">' . $tripStopTimes[0]['arrival_time'] . ' to ' . $tripStopTimes[sizeof($tripStopTimes) - 1]['arrival_time'] . ' ' . $trip['route_long_name'] . ' (' . ucwords($tripStopTimes[0]['service_id']) . ')</li>';
foreach ($tripStopTimes as $key => $tripStopTime) {
- if (($tripStopTimes[$key]["stop_name"] != $tripStopTimes[$key + 1]["stop_name"]) || $key + 1 >= sizeof($tripStopTimes)) {
+ if ($key + 1 > sizeof($tripStopTimes) || ($tripStopTimes[$key]["stop_name"] != $tripStopTimes[$key + 1]["stop_name"])) {
echo '<li>';
if (!startsWith($tripStopTime['stop_code'], "Wj"))
echo '<img src="css/images/time.png" alt="Timing Point" class="ui-li-icon">';
--- a/tripPlanner.php
+++ b/tripPlanner.php
@@ -65,7 +65,7 @@
$leg->from->lon
);
}
- echo '' . staticmap($legMarkers, 0, "iconb", false) . "<br>\n";
+ echo '' . staticmap($legMarkers, false, false, true) . "<br>\n";
echo '<ul>';
foreach ($itinerary->legs->leg as $legNumber => $leg) {
echo '<li>';
@@ -81,7 +81,7 @@
$itinerary->legs->leg->from->lat,
$itinerary->legs->leg->from->lon
)
- ), 0, "iconb", false) . "<br>\n";
+ ), false, false, true) . "<br>\n";
processLeg(0, $itinerary->legs->leg);
}
echo "</p></div>";
@@ -100,7 +100,7 @@
$step->lon
);
}
- echo "" . staticmap($walkStepMarkers, 0, "icong", false) . "<br>\n";
+ echo "" . staticmap($walkStepMarkers, false, false, true) . "<br>\n";
foreach ($leg->steps->walkSteps as $stepNumber => $step) {
echo "Walking step " . ($stepNumber + 1) . ": ";
if ($step->relativeDirection == "CONTINUE") {