ios version
authorRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Wed, 28 Dec 2011 15:47:40 +0000 (16:47 +0100)
committerRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Wed, 28 Dec 2011 15:47:40 +0000 (16:47 +0100)
91 files changed:
.classpath [deleted file]
.gitignore
AndroidManifest.xml [deleted file]
COPYING [deleted file]
Classes/AppDelegate.h [new file with mode: 0644]
Classes/AppDelegate.m [new file with mode: 0644]
NOTICE [deleted file]
PhoneGap.plist [new file with mode: 0644]
PhoneGapBuildSettings.xcconfig [new file with mode: 0644]
Plugins/NativeControls/NativeControls.h [new file with mode: 0644]
Plugins/NativeControls/NativeControls.js [new file with mode: 0644]
Plugins/NativeControls/NativeControls.m [new file with mode: 0644]
Plugins/README [new file with mode: 0644]
Resources/icons/icon-72.png [new file with mode: 0755]
Resources/icons/icon.png [new file with mode: 0755]
Resources/icons/icon@2x.png [new file with mode: 0755]
Resources/splash/Default-Landscape.png [new file with mode: 0755]
Resources/splash/Default-Portrait.png [new file with mode: 0755]
Resources/splash/Default.png [new file with mode: 0755]
Resources/splash/Default@2x.png [new file with mode: 0755]
Wolne Lektury [new file with mode: 0644]
Wolne Lektury.xcodeproj/fnp.mode1v3 [new file with mode: 0644]
Wolne Lektury.xcodeproj/fnp.pbxuser [new file with mode: 0644]
Wolne Lektury.xcodeproj/project.pbxproj [new file with mode: 0755]
assets/www/css/book_text.css [deleted file]
assets/www/css/style.css [deleted file]
assets/www/img/cc-by-sa.png [deleted file]
assets/www/img/cover.jpg [deleted file]
assets/www/img/icon-Book.png [deleted file]
assets/www/img/icon-BookText.png [deleted file]
assets/www/img/icon-Bookmarks.png [deleted file]
assets/www/img/icon-Last.png [deleted file]
assets/www/img/icon-Tag.png [deleted file]
assets/www/img/logo-fnp.png [deleted file]
assets/www/img/logo-wl.png [deleted file]
assets/www/img/procent.png [deleted file]
assets/www/img/spinner.png [deleted file]
assets/www/index.html [deleted file]
assets/www/js/catalogue.js [deleted file]
assets/www/js/dbput.js [deleted file]
assets/www/js/downloader.js [deleted file]
assets/www/js/filerepo.js [deleted file]
assets/www/js/history.js [deleted file]
assets/www/js/links.js [deleted file]
assets/www/js/main.js [deleted file]
assets/www/js/menu.js [deleted file]
assets/www/js/menuinterface.js [deleted file]
assets/www/js/phonegap-1.0.0.js [deleted file]
assets/www/js/view.js [deleted file]
default.properties [deleted file]
gen/pl/org/nowoczesnapolska/wlmobi/R.java [deleted file]
libs/phonegap-1.0.0.jar [deleted file]
main.m [new file with mode: 0644]
proguard.cfg [deleted file]
res/drawable-hdpi/icon.png [deleted file]
res/drawable-ldpi/icon.png [deleted file]
res/drawable-mdpi/icon.png [deleted file]
res/layout/main.xml [deleted file]
res/values/strings.xml [deleted file]
res/xml/plugins.xml [deleted file]
src/pl/org/nowoczesnapolska/wlmobi/Catalogue.java [deleted file]
src/pl/org/nowoczesnapolska/wlmobi/DBPut.java [deleted file]
src/pl/org/nowoczesnapolska/wlmobi/Downloader.java [deleted file]
src/pl/org/nowoczesnapolska/wlmobi/MenuInterface.java [deleted file]
wl_mobi-Info.plist [new file with mode: 0644]
wl_mobi-Prefix.pch [new file with mode: 0644]
www/WolneLektury.html [new file with mode: 0644]
www/css/book_text.css [new file with mode: 0644]
www/css/style.css [new file with mode: 0644]
www/img/cc-by-sa.png [new file with mode: 0644]
www/img/icon-Book.png [new file with mode: 0644]
www/img/icon-BookText.png [new file with mode: 0644]
www/img/icon-Bookmarks.png [new file with mode: 0644]
www/img/icon-Last.png [new file with mode: 0644]
www/img/icon-Tag.png [new file with mode: 0644]
www/img/logo-fnp.png [new file with mode: 0644]
www/img/logo-wl.png [new file with mode: 0644]
www/img/procent.png [new file with mode: 0644]
www/img/spinner.png [new file with mode: 0644]
www/js/NativeControls.js [new file with mode: 0644]
www/js/catalogue.js [new file with mode: 0644]
www/js/dbput.js [new file with mode: 0644]
www/js/downloader.js [new file with mode: 0644]
www/js/filerepo.js [new file with mode: 0644]
www/js/history.js [new file with mode: 0644]
www/js/links.js [new file with mode: 0644]
www/js/main.js [new file with mode: 0644]
www/js/menu.js [new file with mode: 0644]
www/js/menuinterface.js [new file with mode: 0644]
www/js/phonegap-1.1.0.js [new file with mode: 0644]
www/js/view.js [new file with mode: 0644]

diff --git a/.classpath b/.classpath
deleted file mode 100644 (file)
index 1a630a7..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-       <classpathentry kind="src" path="src"/>
-       <classpathentry kind="src" path="gen"/>
-       <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
-       <classpathentry kind="lib" path="libs/phonegap-1.0.0.jar"/>
-       <classpathentry kind="output" path="bin"/>
-</classpath>
index 90786c8..e1a2565 100644 (file)
@@ -5,5 +5,4 @@
 .pydevproject
 .settings
 
-
-
+.DS_Store
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
deleted file mode 100644 (file)
index 9e15f71..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:windowSoftInputMode="adjustPan"
-      package="pl.org.nowoczesnapolska.wlmobi" android:versionName="1.1.1" android:versionCode="4">
-    <supports-screens
-       android:largeScreens="true"
-       android:normalScreens="true"
-       android:smallScreens="true"
-       android:resizeable="true"
-       android:anyDensity="true"
-       />
-
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
-    <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />   
-   
-
-    <application android:icon="@drawable/icon" android:label="@string/app_name"
-       android:debuggable="false">
-        <activity android:name=".Catalogue"
-                  android:label="@string/app_name" android:configChanges="orientation|keyboardHidden">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-    </application>
-
-       <uses-sdk android:minSdkVersion="7" />
-
-</manifest> 
\ No newline at end of file
diff --git a/COPYING b/COPYING
deleted file mode 100644 (file)
index a871fcf..0000000
--- a/COPYING
+++ /dev/null
@@ -1,662 +0,0 @@
-                    GNU AFFERO GENERAL PUBLIC LICENSE
-                       Version 3, 19 November 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-                            Preamble
-
-  The GNU Affero General Public License is a free, copyleft license for
-software and other kinds of works, specifically designed to ensure
-cooperation with the community in the case of network server software.
-
-  The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works.  By contrast,
-our General Public Licenses are intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
-  Developers that use our General Public Licenses protect your rights
-with two steps: (1) assert copyright on the software, and (2) offer
-you this License which gives you legal permission to copy, distribute
-and/or modify the software.
-
-  A secondary benefit of defending all users' freedom is that
-improvements made in alternate versions of the program, if they
-receive widespread use, become available for other developers to
-incorporate.  Many developers of free software are heartened and
-encouraged by the resulting cooperation.  However, in the case of
-software used on network servers, this result may fail to come about.
-The GNU General Public License permits making a modified version and
-letting the public access it on a server without ever releasing its
-source code to the public.
-
-  The GNU Affero General Public License is designed specifically to
-ensure that, in such cases, the modified source code becomes available
-to the community.  It requires the operator of a network server to
-provide the source code of the modified version running there to the
-users of that server.  Therefore, public use of a modified version, on
-a publicly accessible server, gives the public access to the source
-code of the modified version.
-
-  An older license, called the Affero General Public License and
-published by Affero, was designed to accomplish similar goals.  This is
-a different license, not a version of the Affero GPL, but Affero has
-released a new version of the Affero GPL which permits relicensing under
-this license.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-                       TERMS AND CONDITIONS
-
-  0. Definitions.
-
-  "This License" refers to version 3 of the GNU Affero General Public License.
-
-  "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
-  "The Program" refers to any copyrightable work licensed under this
-License.  Each licensee is addressed as "you".  "Licensees" and
-"recipients" may be individuals or organizations.
-
-  To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy.  The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
-  A "covered work" means either the unmodified Program or a work based
-on the Program.
-
-  To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy.  Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
-  To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies.  Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
-  An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License.  If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
-  1. Source Code.
-
-  The "source code" for a work means the preferred form of the work
-for making modifications to it.  "Object code" means any non-source
-form of a work.
-
-  A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
-  The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form.  A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
-  The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities.  However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work.  For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
-  The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
-  The Corresponding Source for a work in source code form is that
-same work.
-
-  2. Basic Permissions.
-
-  All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met.  This License explicitly affirms your unlimited
-permission to run the unmodified Program.  The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work.  This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
-  You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force.  You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright.  Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
-  Conveying under any other circumstances is permitted solely under
-the conditions stated below.  Sublicensing is not allowed; section 10
-makes it unnecessary.
-
-  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
-  No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
-  When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
-  4. Conveying Verbatim Copies.
-
-  You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
-  You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
-  5. Conveying Modified Source Versions.
-
-  You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
-    a) The work must carry prominent notices stating that you modified
-    it, and giving a relevant date.
-
-    b) The work must carry prominent notices stating that it is
-    released under this License and any conditions added under section
-    7.  This requirement modifies the requirement in section 4 to
-    "keep intact all notices".
-
-    c) You must license the entire work, as a whole, under this
-    License to anyone who comes into possession of a copy.  This
-    License will therefore apply, along with any applicable section 7
-    additional terms, to the whole of the work, and all its parts,
-    regardless of how they are packaged.  This License gives no
-    permission to license the work in any other way, but it does not
-    invalidate such permission if you have separately received it.
-
-    d) If the work has interactive user interfaces, each must display
-    Appropriate Legal Notices; however, if the Program has interactive
-    interfaces that do not display Appropriate Legal Notices, your
-    work need not make them do so.
-
-  A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit.  Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
-  6. Conveying Non-Source Forms.
-
-  You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
-    a) Convey the object code in, or embodied in, a physical product
-    (including a physical distribution medium), accompanied by the
-    Corresponding Source fixed on a durable physical medium
-    customarily used for software interchange.
-
-    b) Convey the object code in, or embodied in, a physical product
-    (including a physical distribution medium), accompanied by a
-    written offer, valid for at least three years and valid for as
-    long as you offer spare parts or customer support for that product
-    model, to give anyone who possesses the object code either (1) a
-    copy of the Corresponding Source for all the software in the
-    product that is covered by this License, on a durable physical
-    medium customarily used for software interchange, for a price no
-    more than your reasonable cost of physically performing this
-    conveying of source, or (2) access to copy the
-    Corresponding Source from a network server at no charge.
-
-    c) Convey individual copies of the object code with a copy of the
-    written offer to provide the Corresponding Source.  This
-    alternative is allowed only occasionally and noncommercially, and
-    only if you received the object code with such an offer, in accord
-    with subsection 6b.
-
-    d) Convey the object code by offering access from a designated
-    place (gratis or for a charge), and offer equivalent access to the
-    Corresponding Source in the same way through the same place at no
-    further charge.  You need not require recipients to copy the
-    Corresponding Source along with the object code.  If the place to
-    copy the object code is a network server, the Corresponding Source
-    may be on a different server (operated by you or a third party)
-    that supports equivalent copying facilities, provided you maintain
-    clear directions next to the object code saying where to find the
-    Corresponding Source.  Regardless of what server hosts the
-    Corresponding Source, you remain obligated to ensure that it is
-    available for as long as needed to satisfy these requirements.
-
-    e) Convey the object code using peer-to-peer transmission, provided
-    you inform other peers where the object code and Corresponding
-    Source of the work are being offered to the general public at no
-    charge under subsection 6d.
-
-  A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
-  A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling.  In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage.  For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product.  A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
-  "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source.  The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
-  If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information.  But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
-  The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed.  Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
-  Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
-  7. Additional Terms.
-
-  "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law.  If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
-  When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it.  (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.)  You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
-  Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
-    a) Disclaiming warranty or limiting liability differently from the
-    terms of sections 15 and 16 of this License; or
-
-    b) Requiring preservation of specified reasonable legal notices or
-    author attributions in that material or in the Appropriate Legal
-    Notices displayed by works containing it; or
-
-    c) Prohibiting misrepresentation of the origin of that material, or
-    requiring that modified versions of such material be marked in
-    reasonable ways as different from the original version; or
-
-    d) Limiting the use for publicity purposes of names of licensors or
-    authors of the material; or
-
-    e) Declining to grant rights under trademark law for use of some
-    trade names, trademarks, or service marks; or
-
-    f) Requiring indemnification of licensors and authors of that
-    material by anyone who conveys the material (or modified versions of
-    it) with contractual assumptions of liability to the recipient, for
-    any liability that these contractual assumptions directly impose on
-    those licensors and authors.
-
-  All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10.  If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term.  If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
-  If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
-  Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
-  8. Termination.
-
-  You may not propagate or modify a covered work except as expressly
-provided under this License.  Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
-  However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
-  Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
-  Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License.  If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
-  9. Acceptance Not Required for Having Copies.
-
-  You are not required to accept this License in order to receive or
-run a copy of the Program.  Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance.  However,
-nothing other than this License grants you permission to propagate or
-modify any covered work.  These actions infringe copyright if you do
-not accept this License.  Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
-  10. Automatic Licensing of Downstream Recipients.
-
-  Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License.  You are not responsible
-for enforcing compliance by third parties with this License.
-
-  An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations.  If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
-  You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License.  For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
-  11. Patents.
-
-  A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based.  The
-work thus licensed is called the contributor's "contributor version".
-
-  A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version.  For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
-  Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
-  In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement).  To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
-  If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients.  "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
-  If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
-  A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License.  You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
-  Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
-  12. No Surrender of Others' Freedom.
-
-  If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all.  For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
-  13. Remote Network Interaction; Use with the GNU General Public License.
-
-  Notwithstanding any other provision of this License, if you modify the
-Program, your modified version must prominently offer all users
-interacting with it remotely through a computer network (if your version
-supports such interaction) an opportunity to receive the Corresponding
-Source of your version by providing access to the Corresponding Source
-from a network server at no charge, through some standard or customary
-means of facilitating copying of software.  This Corresponding Source
-shall include the Corresponding Source for any work covered by version 3
-of the GNU General Public License that is incorporated pursuant to the
-following paragraph.
-
-  Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU General Public License into a single
-combined work, and to convey the resulting work.  The terms of this
-License will continue to apply to the part which is the covered work,
-but the work with which it is combined will remain governed by version
-3 of the GNU General Public License.
-
-  14. Revised Versions of this License.
-
-  The Free Software Foundation may publish revised and/or new versions of
-the GNU Affero General Public License from time to time.  Such new versions
-will be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-  Each version is given a distinguishing version number.  If the
-Program specifies that a certain numbered version of the GNU Affero General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation.  If the Program does not specify a version number of the
-GNU Affero General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
-  If the Program specifies that a proxy can decide which future
-versions of the GNU Affero General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
-  Later license versions may give you additional or different
-permissions.  However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
-  15. Disclaimer of Warranty.
-
-  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-  16. Limitation of Liability.
-
-  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
-  17. Interpretation of Sections 15 and 16.
-
-  If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
-                     END OF TERMS AND CONDITIONS
-
-            How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU Affero General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU Affero General Public License for more details.
-
-    You should have received a copy of the GNU Affero General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-Also add information on how to contact you by electronic and paper mail.
-
-  If your software can interact with users remotely through a computer
-network, you should also make sure that it provides a way for users to
-get its source.  For example, if your program is a web application, its
-interface could display a "Source" link that leads users to an archive
-of the code.  There are many ways you could offer source, and different
-solutions will be better for different programs; see section 13 for the
-specific requirements.
-
-  You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU AGPL, see
-<http://www.gnu.org/licenses/>.
-
diff --git a/Classes/AppDelegate.h b/Classes/AppDelegate.h
new file mode 100644 (file)
index 0000000..369da83
--- /dev/null
@@ -0,0 +1,29 @@
+//
+//  AppDelegate.h
+//  wl-mobi
+//
+//  Created by FNP on 10/19/11.
+//  Copyright __MyCompanyName__ 2011. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#ifdef PHONEGAP_FRAMEWORK
+       #import <PhoneGap/PhoneGapDelegate.h>
+#else
+       #import "PhoneGapDelegate.h"
+#endif
+
+@interface AppDelegate : PhoneGapDelegate {
+
+       NSString* invokeString;
+}
+
+// invoke string is passed to your app on launch, this is only valid if you 
+// edit wl-mobi.plist to add a protocol
+// a simple tutorial can be found here : 
+// http://iphonedevelopertips.com/cocoa/launching-your-own-application-via-a-custom-url-scheme.html
+
+@property (copy)  NSString* invokeString;
+
+@end
+
diff --git a/Classes/AppDelegate.m b/Classes/AppDelegate.m
new file mode 100644 (file)
index 0000000..bd627b3
--- /dev/null
@@ -0,0 +1,130 @@
+//
+//  AppDelegate.m
+//  wl-mobi
+//
+//  Created by FNP on 10/19/11.
+//  Copyright __MyCompanyName__ 2011. All rights reserved.
+//
+
+#import "AppDelegate.h"
+#ifdef PHONEGAP_FRAMEWORK
+       #import <PhoneGap/PhoneGapViewController.h>
+#else
+       #import "PhoneGapViewController.h"
+#endif
+
+@implementation AppDelegate
+
+@synthesize invokeString;
+
+- (id) init
+{      
+       /** If you need to do any extra app-specific initialization, you can do it here
+        *  -jm
+        **/
+    return [super init];
+}
+
+/**
+ * This is main kick off after the app inits, the views and Settings are setup here. (preferred - iOS4 and up)
+ */
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
+{
+       
+       NSArray *keyArray = [launchOptions allKeys];
+       if ([launchOptions objectForKey:[keyArray objectAtIndex:0]]!=nil) 
+       {
+               NSURL *url = [launchOptions objectForKey:[keyArray objectAtIndex:0]];
+               self.invokeString = [url absoluteString];
+               NSLog(@"wl-mobi launchOptions = %@",url);
+       }
+       
+       return [super application:application didFinishLaunchingWithOptions:launchOptions];
+}
+
+// this happens while we are running ( in the background, or from within our own app )
+// only valid if wl-mobi.plist specifies a protocol to handle
+- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url 
+{
+    // must call super so all plugins will get the notification, and their handlers will be called 
+       // super also calls into javascript global function 'handleOpenURL'
+    return [super application:application handleOpenURL:url];
+}
+
+-(id) getCommandInstance:(NSString*)className
+{
+       /** You can catch your own commands here, if you wanted to extend the gap: protocol, or add your
+        *  own app specific protocol to it. -jm
+        **/
+       return [super getCommandInstance:className];
+}
+
+/**
+ Called when the webview finishes loading.  This stops the activity view and closes the imageview
+ */
+- (void)webViewDidFinishLoad:(UIWebView *)theWebView 
+{
+       // only valid if wl-mobi.plist specifies a protocol to handle
+       if(self.invokeString)
+       {
+               // this is passed before the deviceready event is fired, so you can access it in js when you receive deviceready
+               NSString* jsString = [NSString stringWithFormat:@"var invokeString = \"%@\";", self.invokeString];
+               [theWebView stringByEvaluatingJavaScriptFromString:jsString];
+       }
+       return [ super webViewDidFinishLoad:theWebView ];
+}
+
+- (void)webViewDidStartLoad:(UIWebView *)theWebView 
+{
+       return [ super webViewDidStartLoad:theWebView ];
+}
+
+/**
+ * Fail Loading With Error
+ * Error - If the webpage failed to load display an error with the reason.
+ */
+- (void)webView:(UIWebView *)theWebView didFailLoadWithError:(NSError *)error 
+{
+       return [ super webView:theWebView didFailLoadWithError:error ];
+}
+
+
+/**
+ * Start Loading Request
+ * This is where most of the magic happens... We take the request(s) and process the response.
+ * From here we can re direct links and other protocalls to different internal methods.
+ */
+- (BOOL)webView:(UIWebView *)theWebView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
+{
+       NSURL *url = [request URL];
+       
+       // Intercept the external http requests and forward to Safari.app
+       // Otherwise forward to the PhoneGap WebView
+       if ([[url scheme] isEqualToString:@"http"] || [[url scheme] isEqualToString:@"https"]) {
+               [[UIApplication sharedApplication] openURL:url];
+               return NO;
+       }
+       else {
+               return [ super webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType ];
+       }
+}
+
+
+
+- (BOOL) execute:(InvokedUrlCommand*)command
+{
+       return [ super execute:command];
+}
+
+- (void)dealloc
+{
+       [ super dealloc ];
+}
+
+
++ (NSString*) startPage 
+{ 
+       return @"WolneLektury.html"; 
+}
+
+@end
diff --git a/NOTICE b/NOTICE
deleted file mode 100644 (file)
index 73a186e..0000000
--- a/NOTICE
+++ /dev/null
@@ -1,19 +0,0 @@
-    
-    WolneLektury-Mobile
-
-    Copyright © 2011 Fundacja Nowoczesna Polska <fundacja@nowoczesnapolska.org.pl>
-    
-    Author: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU Affero General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU Affero General Public License for more details.
-
-    You should have received a copy of the GNU Affero General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
diff --git a/PhoneGap.plist b/PhoneGap.plist
new file mode 100644 (file)
index 0000000..39c98ef
--- /dev/null
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>DetectPhoneNumber</key>
+       <false/>
+       <key>TopActivityIndicator</key>
+       <string>gray</string>
+       <key>EnableLocation</key>
+       <false/>
+       <key>EnableAcceleration</key>
+       <false/>
+       <key>EnableViewportScale</key>
+       <false/>
+       <key>AutoHideSplashScreen</key>
+       <true/>
+       <key>ShowSplashScreenSpinner</key>
+       <true/>
+       <key>MediaPlaybackRequiresUserAction</key>
+       <false/>
+       <key>AllowInlineMediaPlayback</key>
+       <false/>
+       <key>ExternalHosts</key>
+       <array>
+               <string>wolnelektury.pl</string>
+               <string>www.wolnelektury.pl</string>
+               <string>static.wolnelektury.pl</string>
+       </array>
+       <key>Plugins</key>
+       <dict>
+               <key>NativeControls</key>
+               <string>NativeControls</string>
+               <key>com.phonegap.accelerometer</key>
+               <string>PGAccelerometer</string>
+               <key>com.phonegap.camera</key>
+               <string>PGCamera</string>
+               <key>com.phonegap.connection</key>
+               <string>PGConnection</string>
+               <key>com.phonegap.contacts</key>
+               <string>PGContacts</string>
+               <key>com.phonegap.debugconsole</key>
+               <string>PGDebugConsole</string>
+               <key>com.phonegap.file</key>
+               <string>PGFile</string>
+               <key>com.phonegap.filetransfer</key>
+               <string>PGFileTransfer</string>
+               <key>com.phonegap.geolocation</key>
+               <string>PGLocation</string>
+               <key>com.phonegap.notification</key>
+               <string>PGNotification</string>
+               <key>com.phonegap.media</key>
+               <string>PGSound</string>
+               <key>com.phonegap.mediacapture</key>
+               <string>PGCapture</string>
+               <key>com.phonegap.splashscreen</key>
+               <string>PGSplashScreen</string>
+       </dict>
+</dict>
+</plist>
diff --git a/PhoneGapBuildSettings.xcconfig b/PhoneGapBuildSettings.xcconfig
new file mode 100644 (file)
index 0000000..83badbb
--- /dev/null
@@ -0,0 +1,8 @@
+//
+//  PhoneGap.xcconfig
+//  wl-mobi
+//
+
+// Override this to use your project specific PHONEGAPLIB. 
+// You can base it off the current project path, $(PROJECT_DIR)
+PHONEGAPLIB = $(PHONEGAPLIB)
\ No newline at end of file
diff --git a/Plugins/NativeControls/NativeControls.h b/Plugins/NativeControls/NativeControls.h
new file mode 100644 (file)
index 0000000..31976ce
--- /dev/null
@@ -0,0 +1,59 @@
+//
+//  NativeControls.h
+//
+//
+//  Created by Jesse MacFadyen on 10-02-03.
+//  MIT Licensed
+
+//  Originally this code was developed my Michael Nachbaur
+//  Formerly -> PhoneGap :: UIControls.h
+//  Created by Michael Nachbaur on 13/04/09.
+//  Copyright 2009 Decaf Ninja Software. All rights reserved.
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+#import <UIKit/UITabBar.h>
+#import <UIKit/UIToolbar.h>
+#ifdef PHONEGAP_FRAMEWORK
+#import <PhoneGap/PGPlugin.h>
+#else
+#import "PGPlugin.h"
+#endif
+
+@interface NativeControls : PGPlugin <UITabBarDelegate, UIActionSheetDelegate> {
+       UITabBar* tabBar;
+       NSMutableDictionary* tabBarItems;
+    
+       UIToolbar* toolBar;
+       UIBarButtonItem* toolBarTitle;
+       NSMutableArray* toolBarItems;
+    
+       CGRect  originalWebViewBounds;
+}
+
+/* Tab Bar methods
+ */
+- (void)createTabBar:(NSArray*)arguments withDict:(NSDictionary*)options;
+- (void)showTabBar:(NSArray*)arguments withDict:(NSDictionary*)options;
+- (void)hideTabBar:(NSArray*)arguments withDict:(NSDictionary*)options;
+- (void)showTabBarItems:(NSArray*)arguments withDict:(NSDictionary*)options;
+- (void)createTabBarItem:(NSArray*)arguments withDict:(NSDictionary*)options;
+- (void)updateTabBarItem:(NSArray*)arguments withDict:(NSDictionary*)options;
+- (void)selectTabBarItem:(NSArray*)arguments withDict:(NSDictionary*)options;
+
+
+
+/* Tool Bar methods
+ */
+- (void)createToolBar:(NSArray*)arguments withDict:(NSDictionary*)options;
+- (void)resetToolBar:(NSArray*)arguments withDict:(NSDictionary*)options;
+- (void)setToolBarTitle:(NSArray*)arguments withDict:(NSDictionary*)options;
+- (void)createToolBarItem:(NSArray*)arguments withDict:(NSDictionary*)options;
+- (void)showToolBar:(NSArray*)arguments withDict:(NSDictionary*)options;
+- (void)hideToolBar:(NSArray*)arguments withDict:(NSDictionary*)options;
+/* ActionSheet
+ */
+- (void)createActionSheet:(NSArray*)arguments withDict:(NSDictionary*)options;
+
+
+@end
\ No newline at end of file
diff --git a/Plugins/NativeControls/NativeControls.js b/Plugins/NativeControls/NativeControls.js
new file mode 100644 (file)
index 0000000..6e6d3c3
--- /dev/null
@@ -0,0 +1,309 @@
+// JS ::::::::
+/*
+ //  This code is adapted from the work of:
+ //  Created by Michael Nachbaur on 13/04/09.
+ //  Copyright 2009 Decaf Ninja Software. All rights reserved.
+ //  MIT licensed
+ */
+/**
+ * This class exposes mobile phone interface controls to JavaScript, such as
+ * native tab and tool bars, etc.
+ * @constructor
+ */
+function NativeControls() {
+    this.tabBarTag = 0;
+    this.toolBarIndexes = 0;
+   
+    this.tabBarCallbacks = {};
+    this.toolBarCallbacks = {};
+   
+    this.tappedToolBarItem = null;
+    this.selectedTabBarItem = null;
+}
+/**
+ * Create a native tab bar that can have tab buttons added to it which can respond to events.
+ */
+NativeControls.prototype.createTabBar = function() {
+    PhoneGap.exec("NativeControls.createTabBar");
+};
+/**
+ * Show a tab bar.  The tab bar has to be created first.
+ * @param {Object} [options] Options indicating how the tab bar should be shown:
+ * - \c height integer indicating the height of the tab bar (default: \c 49)
+ * - \c position specifies whether the tab bar will be placed at the \c top or \c bottom of the screen (default: \c bottom)
+ */
+NativeControls.prototype.showTabBar = function(options) {
+    if (!options) options = {'position' : 'bottom'};
+    PhoneGap.exec("NativeControls.showTabBar", options);
+};
+/**
+ * Hide a tab bar.  The tab bar has to be created first.
+ */
+NativeControls.prototype.hideTabBar = function(animate) {
+    if (animate == undefined || animate == null)
+        animate = true;
+    PhoneGap.exec("NativeControls.hideTabBar", { animate: animate });
+};
+/**
+ * Create a new tab bar item for use on a previously created tab bar.  Use ::showTabBarItems to show the new item on the tab bar.
+ *
+ * If the supplied image name is one of the labels listed below, then this method will construct a tab button
+ * using the standard system buttons.  Note that if you use one of the system images, that the \c title you supply will be ignored.
+ *
+ * <b>Tab Buttons</b>
+ *   - tabButton:More
+ *   - tabButton:Favorites
+ *   - tabButton:Featured
+ *   - tabButton:TopRated
+ *   - tabButton:Recents
+ *   - tabButton:Contacts
+ *   - tabButton:History
+ *   - tabButton:Bookmarks
+ *   - tabButton:Search
+ *   - tabButton:Downloads
+ *   - tabButton:MostRecent
+ *   - tabButton:MostViewed
+ * @param {String} name internal name to refer to this tab by
+ * @param {String} [title] title text to show on the tab, or null if no text should be shown
+ * @param {String} [image] image filename or internal identifier to show, or null if now image should be shown
+ * @param {Object} [options] Options for customizing the individual tab item
+ *  - \c badge value to display in the optional circular badge on the item; if null or unspecified, the badge will be hidden
+ */
+NativeControls.prototype.createTabBarItem = function(name, label, image, options) {
+   
+        var tag = this.tabBarTag++;
+    if (options && 'onSelect' in options && typeof(options['onSelect']) == 'function') {
+        this.tabBarCallbacks[tag] = {'onSelect':options.onSelect,'name':name};
+        //delete options.onSelect;
+    }
+       
+    PhoneGap.exec("NativeControls.createTabBarItem", name, label, image, tag, options);
+};
+/**
+ * Update an existing tab bar item to change its badge value.
+ * @param {String} name internal name used to represent this item when it was created
+ * @param {Object} options Options for customizing the individual tab item
+ *  - \c badge value to display in the optional circular badge on the item; if null or unspecified, the badge will be hidden
+ */
+NativeControls.prototype.updateTabBarItem = function(name, options) {
+    if (!options) options = {};
+    PhoneGap.exec("NativeControls.updateTabBarItem", name, options);
+};
+/**
+ * Show previously created items on the tab bar
+ * @param {String} arguments... the item names to be shown
+ * @param {Object} [options] dictionary of options, notable options including:
+ *  - \c animate indicates that the items should animate onto the tab bar
+ * @see createTabBarItem
+ * @see createTabBar
+ */
+NativeControls.prototype.showTabBarItems = function() {
+    var parameters = [ "NativeControls.showTabBarItems" ];
+    for (var i = 0; i < arguments.length; i++) {
+        parameters.push(arguments[i]);
+    }
+    PhoneGap.exec.apply(this, parameters);
+};
+/**
+ * Function to detect currently selected tab bar item
+ * @see createTabBarItem
+ * @see showTabBarItems
+ */
+NativeControls.prototype.getSelectedTabBarItem = function() {
+    return this.selectedTabBarItem;
+};
+/**
+ * Manually select an individual tab bar item, or nil for deselecting a currently selected tab bar item.
+ * @param {String} tabName the name of the tab to select, or null if all tabs should be deselected
+ * @see createTabBarItem
+ * @see showTabBarItems
+ */
+NativeControls.prototype.selectTabBarItem = function(tab) {
+    PhoneGap.exec("NativeControls.selectTabBarItem", tab);
+};
+/**
+ * Function called when a tab bar item has been selected.
+ * @param {Number} tag the tag number for the item that has been selected
+ */
+NativeControls.prototype.tabBarItemSelected = function(tag)
+{
+        this.selectedTabBarItem = tag;
+    if (typeof(this.tabBarCallbacks[tag].onSelect) == 'function')
+        this.tabBarCallbacks[tag].onSelect(this.tabBarCallbacks[tag].name);
+};
+/**
+ * Create a toolbar.
+ */
+NativeControls.prototype.createToolBar = function()
+{
+    PhoneGap.exec("NativeControls.createToolBar");
+};
+/**
+ * Function called when a tab bar item has been selected.
+ * @param {String} title the title to set within the toolbar
+ */
+NativeControls.prototype.setToolBarTitle = function(title)
+{
+    PhoneGap.exec("NativeControls.setToolBarTitle", title);
+};
+/*
+ * Added by Emile khattar: emile818@gmail.com emile@sign.al
+ * @ 2011-07-08 ,  5.00 AM
+ */
+/**
+ * Set toolBarItems = nil;
+ */
+NativeControls.prototype.resetToolBar = function() {
+    PhoneGap.exec("NativeControls.resetToolBar");
+};
+/**
+ * Hide the tool bar
+ * @brief hide the tool bar
+ */
+NativeControls.prototype.hideToolBar = function() {
+    PhoneGap.exec("NativeControls.hideToolBar");
+};
+/**
+ * Show the tool bar ( re-render elements )
+ * @brief Show the tool bar
+ */
+NativeControls.prototype.showToolBar = function() {
+    PhoneGap.exec("NativeControls.showToolBar");
+};
+/**
+ * Set the toolbar title
+ * @param: title
+ */
+NativeControls.prototype.setToolBarTitle = function(title) {
+    PhoneGap.exec("NativeControls.setToolBarTitle" , title );
+};
+/**
+ * Create a new tool bar button item for use on a previously created tool bar.  Use ::showToolBar to show the new item on the tool bar.
+ *
+ * If the supplied image name is one of the labels listed below, then this method will construct a button
+ * using the standard system buttons.  Note that if you use one of the system images, that the title you supply will be ignored.
+ *
+ * <b>Tool Bar Buttons</b>
+ * UIBarButtonSystemItemDone
+ * UIBarButtonSystemItemCancel
+ * UIBarButtonSystemItemEdit
+ * UIBarButtonSystemItemSave
+ * UIBarButtonSystemItemAdd
+ * UIBarButtonSystemItemFlexibleSpace
+ * UIBarButtonSystemItemFixedSpace
+ * UIBarButtonSystemItemCompose
+ * UIBarButtonSystemItemReply
+ * UIBarButtonSystemItemAction
+ * UIBarButtonSystemItemOrganize
+ * UIBarButtonSystemItemBookmarks
+ * UIBarButtonSystemItemSearch
+ * UIBarButtonSystemItemRefresh
+ * UIBarButtonSystemItemStop
+ * UIBarButtonSystemItemCamera
+ * UIBarButtonSystemItemTrash
+ * UIBarButtonSystemItemPlay
+ * UIBarButtonSystemItemPause
+ * UIBarButtonSystemItemRewind
+ * UIBarButtonSystemItemFastForward
+ * UIBarButtonSystemItemUndo,        // iOS 3.0 and later
+ * UIBarButtonSystemItemRedo,        // iOS 3.0 and later
+ * UIBarButtonSystemItemPageCurl,    // iOS 4.0 and later
+ * @param {String} name internal name to refer to this tab by
+ * @param {String} [title] title text to show on the button, or null if no text should be shown
+ * @param {String} [image] image filename or internal identifier to show, or null if now image should be shown
+ * @param {Object} [options] Options for customizing the individual tab item [no option available at this time - this is for future proofing]
+ *  
+ */
+NativeControls.prototype.createToolBarItem = function(name , title , image , options) {
+        var toolBarIndex = this.toolBarIndexes++;
+        if (options && 'onTap' in options && typeof(options['onTap']) == 'function') {
+        this.toolBarCallbacks[toolBarIndex] = {'onTap':options.onTap,'name':name};
+        //delete options.onSelect;
+    }
+        //modify the NativeControls.m to change the options quickly
+        // the instance name on the plugin can be passed with option for now it is hardcode in objc // Emile
+    PhoneGap.exec("NativeControls.createToolBarItem" , name , title , image , options );
+};
+/**
+ * Function called when a tool bar item has been tapped.
+ * @param {Number} tag the tag number for the item that has been selected
+ */
+NativeControls.prototype.toolBarButtonTapped = function(tag)
+{
+        this.tappedToolBarItem = tag;
+    if (typeof(this.toolBarCallbacks[tag].onTap) == 'function')
+        this.toolBarCallbacks[tag].onTap(this.toolBarCallbacks[tag].name);
+};
+NativeControls.prototype.createActionSheet = function(buttonTitles,actionSheetTitle,cancelButtonIndex,destructiveButtonIndex)
+{
+        var options = {};
+       
+        if(actionSheetTitle != null)
+        {
+                options.title = actionSheetTitle;
+        }
+        if(cancelButtonIndex != null)
+        {
+                options.cancelButtonIndex = cancelButtonIndex;
+        }
+        if(destructiveButtonIndex != null)
+        {
+                options.destructiveButtonIndex = destructiveButtonIndex;
+        }
+   
+        var params = [ "NativeControls.createActionSheet",options ];
+    for (var i = 0; i < buttonTitles.length; i++)
+        {
+        params.push(buttonTitles[i]);
+    }
+    PhoneGap.exec.apply(this, params);
+       
+        this.actionSheetDelegate = {};
+        return this.actionSheetDelegate;
+}
+NativeControls.prototype._onActionSheetDismissed = function(index)
+{
+        this.actionSheetDelegate.onActionSheetDismissed(index);
+}
+NativeControls.prototype.setStatusBarVisibilty = function(bHide)
+{
+        PhoneGap.exec("StatusBar.setHidden",bHide);
+}
+if(!window.plugins)
+  window.plugins = {};
+ window.plugins.nativeControls = new NativeControls();
diff --git a/Plugins/NativeControls/NativeControls.m b/Plugins/NativeControls/NativeControls.m
new file mode 100644 (file)
index 0000000..8f89c6d
--- /dev/null
@@ -0,0 +1,704 @@
+//
+//  NativeControls.h
+//  
+//
+//  Created by Jesse MacFadyen on 10-02-03.
+//  MIT Licensed
+
+//  Originally this code was developed my Michael Nachbaur
+//  Formerly -> PhoneGap :: UIControls.h
+//  Created by Michael Nachbaur on 13/04/09.
+//  Copyright 2009 Decaf Ninja Software. All rights reserved.
+
+#import "NativeControls.h"
+
+#import <QuartzCore/QuartzCore.h>
+
+@implementation NativeControls
+#ifndef __IPHONE_3_0
+@synthesize webView;
+#endif
+
+-(PGPlugin*) initWithWebView:(UIWebView*)theWebView
+{
+    self = (NativeControls*)[super initWithWebView:theWebView];
+    if (self) 
+       {
+        tabBarItems = [[NSMutableDictionary alloc] initWithCapacity:5];
+               originalWebViewBounds = theWebView.bounds;
+    }
+    return self;
+}
+
+- (void)dealloc
+{      
+    if (tabBar)
+        [tabBar release];
+       
+       if (toolBar)
+       {
+               [toolBarTitle release];
+               [toolBarItems release];
+               [toolBar release];
+       }
+       
+    [super dealloc];
+}
+
+#pragma mark -
+#pragma mark TabBar
+
+/**
+ * Create a native tab bar at either the top or the bottom of the display.
+ * @brief creates a tab bar
+ * @param arguments unused
+ * @param options unused
+ */
+- (void)createTabBar:(NSArray*)arguments withDict:(NSDictionary*)options
+{
+    tabBar = [UITabBar new];
+    [tabBar sizeToFit];
+    tabBar.delegate = self;
+    tabBar.multipleTouchEnabled   = NO;
+    tabBar.autoresizesSubviews    = YES;
+    tabBar.hidden                 = YES;
+    tabBar.userInteractionEnabled = YES;
+       tabBar.opaque = YES;
+       
+       self.webView.superview.autoresizesSubviews = YES;
+       
+       [ self.webView.superview addSubview:tabBar];    
+}
+
+/**
+ * Show the tab bar after its been created.
+ * @brief show the tab bar
+ * @param arguments unused
+ * @param options used to indicate options for where and how the tab bar should be placed
+ * - \c height integer indicating the height of the tab bar (default: \c 49)
+ * - \c position specifies whether the tab bar will be placed at the \c top or \c bottom of the screen (default: \c bottom)
+ */
+- (void)showTabBar:(NSArray*)arguments withDict:(NSDictionary*)options
+{
+    if (!tabBar)
+        [self createTabBar:nil withDict:nil];
+       
+       // if we are calling this again when its shown, reset
+       if (!tabBar.hidden) {
+               return;
+       }
+    
+    CGFloat height = 0.0f;
+    BOOL atBottom = YES;
+       
+    // CGRect offsetRect = [ [UIApplication sharedApplication] statusBarFrame];
+    
+    if (options) 
+       {
+        height   = [[options objectForKey:@"height"] floatValue];
+        atBottom = [[options objectForKey:@"position"] isEqualToString:@"bottom"];
+    }
+       if(height == 0)
+       {
+               height = 49.0f;
+               atBottom = YES;
+       }
+       
+    tabBar.hidden = NO;
+    CGRect webViewBounds = originalWebViewBounds;
+    CGRect tabBarBounds;
+       
+       NSNotification* notif = [NSNotification notificationWithName:@"PGLayoutSubviewAdded" object:tabBar];
+       [[NSNotificationQueue defaultQueue] enqueueNotification:notif postingStyle: NSPostASAP];
+       
+    if (atBottom) 
+    {
+        tabBarBounds = CGRectMake(
+                                  webViewBounds.origin.x,
+                                  webViewBounds.origin.y + webViewBounds.size.height - height,
+                                  webViewBounds.size.width,
+                                  height
+                                  );
+        webViewBounds = CGRectMake(
+                                   webViewBounds.origin.x,
+                                   webViewBounds.origin.y,
+                                   webViewBounds.size.width,
+                                   webViewBounds.size.height - height
+                                   );
+    } 
+    else 
+    {
+        tabBarBounds = CGRectMake(
+                                  webViewBounds.origin.x,
+                                  webViewBounds.origin.y,
+                                  webViewBounds.size.width,
+                                  height
+                                  );
+        webViewBounds = CGRectMake(
+                                   webViewBounds.origin.x,
+                                   webViewBounds.origin.y + height,
+                                   webViewBounds.size.width,
+                                   webViewBounds.size.height - height
+                                   );
+    }
+    
+    [tabBar setFrame:tabBarBounds];
+       
+       
+    [self.webView setFrame:webViewBounds];
+}
+
+/**
+ * Hide the tab bar
+ * @brief hide the tab bar
+ * @param arguments unused
+ * @param options unused
+ */
+- (void)hideTabBar:(NSArray*)arguments withDict:(NSDictionary*)options
+{
+    if (!tabBar)
+        [self createTabBar:nil withDict:nil];
+    tabBar.hidden = YES;
+       
+       NSNotification* notif = [NSNotification notificationWithName:@"PGLayoutSubviewRemoved" object:tabBar];
+       [[NSNotificationQueue defaultQueue] enqueueNotification:notif postingStyle: NSPostASAP];
+       
+       
+       [self.webView setFrame:originalWebViewBounds];
+}
+
+/**
+ * Create a new tab bar item for use on a previously created tab bar.  Use ::showTabBarItems to show the new item on the tab bar.
+ *
+ * If the supplied image name is one of the labels listed below, then this method will construct a tab button
+ * using the standard system buttons.  Note that if you use one of the system images, that the \c title you supply will be ignored.
+ * - <b>Tab Buttons</b>
+ *   - tabButton:More
+ *   - tabButton:Favorites
+ *   - tabButton:Featured
+ *   - tabButton:TopRated
+ *   - tabButton:Recents
+ *   - tabButton:Contacts
+ *   - tabButton:History
+ *   - tabButton:Bookmarks
+ *   - tabButton:Search
+ *   - tabButton:Downloads
+ *   - tabButton:MostRecent
+ *   - tabButton:MostViewed
+ * @brief create a tab bar item
+ * @param arguments Parameters used to create the tab bar
+ *  -# \c name internal name to refer to this tab by
+ *  -# \c title title text to show on the tab, or null if no text should be shown
+ *  -# \c image image filename or internal identifier to show, or null if now image should be shown
+ *  -# \c tag unique number to be used as an internal reference to this button
+ * @param options Options for customizing the individual tab item
+ *  - \c badge value to display in the optional circular badge on the item; if nil or unspecified, the badge will be hidden
+ */
+- (void)createTabBarItem:(NSArray*)arguments withDict:(NSDictionary*)options
+{
+    if (!tabBar)
+        [self createTabBar:nil withDict:nil];
+    
+    NSString  *name      = [arguments objectAtIndex:0];
+    NSString  *title     = [arguments objectAtIndex:1];
+    NSString  *imageName = [arguments objectAtIndex:2];
+    int tag              = [[arguments objectAtIndex:3] intValue];
+    
+    UITabBarItem *item = nil;    
+    if ([imageName length] > 0) {
+        UIBarButtonSystemItem systemItem = -1;
+        if ([imageName isEqualToString:@"tabButton:More"])       systemItem = UITabBarSystemItemMore;
+        if ([imageName isEqualToString:@"tabButton:Favorites"])  systemItem = UITabBarSystemItemFavorites;
+        if ([imageName isEqualToString:@"tabButton:Featured"])   systemItem = UITabBarSystemItemFeatured;
+        if ([imageName isEqualToString:@"tabButton:TopRated"])   systemItem = UITabBarSystemItemTopRated;
+        if ([imageName isEqualToString:@"tabButton:Recents"])    systemItem = UITabBarSystemItemRecents;
+        if ([imageName isEqualToString:@"tabButton:Contacts"])   systemItem = UITabBarSystemItemContacts;
+        if ([imageName isEqualToString:@"tabButton:History"])    systemItem = UITabBarSystemItemHistory;
+        if ([imageName isEqualToString:@"tabButton:Bookmarks"])  systemItem = UITabBarSystemItemBookmarks;
+        if ([imageName isEqualToString:@"tabButton:Search"])     systemItem = UITabBarSystemItemSearch;
+        if ([imageName isEqualToString:@"tabButton:Downloads"])  systemItem = UITabBarSystemItemDownloads;
+        if ([imageName isEqualToString:@"tabButton:MostRecent"]) systemItem = UITabBarSystemItemMostRecent;
+        if ([imageName isEqualToString:@"tabButton:MostViewed"]) systemItem = UITabBarSystemItemMostViewed;
+        if (systemItem != -1)
+            item = [[UITabBarItem alloc] initWithTabBarSystemItem:systemItem tag:tag];
+    }
+    
+    if (item == nil) {
+        item = [[UITabBarItem alloc] initWithTitle:title image:[UIImage imageNamed:imageName] tag:tag];
+    }
+    
+    if ([options objectForKey:@"badge"])
+        item.badgeValue = [options objectForKey:@"badge"];
+    
+    [tabBarItems setObject:item forKey:name];
+       [item release];
+}
+
+
+/**
+ * Update an existing tab bar item to change its badge value.
+ * @brief update the badge value on an existing tab bar item
+ * @param arguments Parameters used to identify the tab bar item to update
+ *  -# \c name internal name used to represent this item when it was created
+ * @param options Options for customizing the individual tab item
+ *  - \c badge value to display in the optional circular badge on the item; if nil or unspecified, the badge will be hidden
+ */
+- (void)updateTabBarItem:(NSArray*)arguments withDict:(NSDictionary*)options
+{
+    if (!tabBar)
+        [self createTabBar:nil withDict:nil];
+    
+    NSString  *name = [arguments objectAtIndex:0];
+    UITabBarItem *item = [tabBarItems objectForKey:name];
+    if (item)
+        item.badgeValue = [options objectForKey:@"bad   ge"];
+}
+
+
+/**
+ * Show previously created items on the tab bar
+ * @brief show a list of tab bar items
+ * @param arguments the item names to be shown
+ * @param options dictionary of options, notable options including:
+ *  - \c animate indicates that the items should animate onto the tab bar
+ * @see createTabBarItem
+ * @see createTabBar
+ */
+- (void)showTabBarItems:(NSArray*)arguments withDict:(NSDictionary*)options
+{
+    if (!tabBar)
+        [self createTabBar:nil withDict:nil];
+    
+    int i, count = [arguments count];
+    NSMutableArray *items = [[NSMutableArray alloc] initWithCapacity:count];
+    for (i = 0; i < count; i++) {
+        NSString *itemName = [arguments objectAtIndex:i];
+        UITabBarItem *item = [tabBarItems objectForKey:itemName];
+        if (item)
+            [items addObject:item];
+    }
+    
+    BOOL animateItems = NO;
+    if ([options objectForKey:@"animate"])
+        animateItems = [(NSString*)[options objectForKey:@"animate"] boolValue];
+    [tabBar setItems:items animated:animateItems];
+       [items release];
+}
+
+/**
+ * Manually select an individual tab bar item, or nil for deselecting a currently selected tab bar item.
+ * @brief manually select a tab bar item
+ * @param arguments the name of the tab bar item to select
+ * @see createTabBarItem
+ * @see showTabBarItems
+ */
+- (void)selectTabBarItem:(NSArray*)arguments withDict:(NSDictionary*)options
+{
+    if (!tabBar)
+        [self createTabBar:nil withDict:nil];
+    
+    NSString *itemName = [arguments objectAtIndex:0];
+    UITabBarItem *item = [tabBarItems objectForKey:itemName];
+    if (item)
+        tabBar.selectedItem = item;
+    else
+        tabBar.selectedItem = nil;
+}
+
+
+- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
+{
+    NSString * jsCallBack = [NSString stringWithFormat:@"window.plugins.nativeControls.tabBarItemSelected(%d);", item.tag];    
+    [self.webView stringByEvaluatingJavaScriptFromString:jsCallBack];
+}
+
+#pragma mark -
+#pragma mark ToolBar
+
+
+/*********************************************************************************/
+- (void)createToolBar:(NSArray*)arguments withDict:(NSDictionary*)options
+{
+    CGFloat height   = 45.0f;
+    BOOL atTop       = YES;
+    UIBarStyle style = UIBarStyleBlackOpaque;
+    
+    NSDictionary* toolBarSettings = options;//[settings objectForKey:@"ToolBarSettings"];
+    if (toolBarSettings) 
+       {
+        if ([toolBarSettings objectForKey:@"height"])
+            height = [[toolBarSettings objectForKey:@"height"] floatValue];
+               
+        if ([toolBarSettings objectForKey:@"position"])
+            atTop  = [[toolBarSettings objectForKey:@"position"] isEqualToString:@"top"];
+        
+#pragma unused(atTop)
+               
+        NSString *styleStr = [toolBarSettings objectForKey:@"style"];
+        if ([styleStr isEqualToString:@"Default"])
+            style = UIBarStyleDefault;
+        else if ([styleStr isEqualToString:@"BlackOpaque"])
+            style = UIBarStyleBlackOpaque;
+        else if ([styleStr isEqualToString:@"BlackTranslucent"])
+            style = UIBarStyleBlackTranslucent;
+    }
+    
+    CGRect webViewBounds = self.webView.bounds;
+    CGRect toolBarBounds = CGRectMake(
+                                      webViewBounds.origin.x,
+                                      webViewBounds.origin.y - 1.0f,
+                                      webViewBounds.size.width,
+                                      height
+                                      );
+    webViewBounds = CGRectMake(
+                               webViewBounds.origin.x,
+                               webViewBounds.origin.y + height,
+                               webViewBounds.size.width,
+                               webViewBounds.size.height - height
+                               );
+    toolBar = [[UIToolbar alloc] initWithFrame:toolBarBounds];
+    [toolBar sizeToFit];
+    toolBar.hidden                 = NO;
+    toolBar.multipleTouchEnabled   = NO;
+    toolBar.autoresizesSubviews    = YES;
+    toolBar.userInteractionEnabled = YES;
+    toolBar.barStyle               = style;
+       
+    
+    [toolBar setFrame:toolBarBounds];
+    [self.webView setFrame:webViewBounds];
+    
+    [self.webView.superview addSubview:toolBar];
+}
+
+- (void)resetToolBar:(NSArray*)arguments withDict:(NSDictionary*)options
+{
+       NSLog(@"about to reset toolBarItems");
+       toolBarItems = nil;
+       /*
+     if (toolBarItems)
+     {
+     [toolBarItems release];
+     }
+        */
+}
+
+/**
+ * Hide the tool bar
+ * @brief hide the tool bar
+ * @param arguments unused
+ * @param options unused
+ */
+- (void)hideToolBar:(NSArray*)arguments withDict:(NSDictionary*)options
+{
+    if (!toolBar)
+        [self createToolBar:nil withDict:nil];
+    toolBar.hidden = YES;
+       
+       NSNotification* notif = [NSNotification notificationWithName:@"PGLayoutSubviewRemoved" object:toolBar];
+       [[NSNotificationQueue defaultQueue] enqueueNotification:notif postingStyle: NSPostASAP];
+       
+       
+       [self.webView setFrame:originalWebViewBounds];
+}
+
+
+- (void)setToolBarTitle:(NSArray*)arguments withDict:(NSDictionary*)options
+{
+    if (!toolBar)
+        [self createToolBar:nil withDict:nil];
+    
+    NSString *title = [arguments objectAtIndex:0];
+    
+       
+    if (!toolBarTitle) {
+         NSLog(@"not : %@", title);
+        toolBarTitle = [[UIBarButtonItem alloc] initWithTitle:title style:UIBarButtonItemStylePlain target:self action:@selector(toolBarTitleClicked)];
+    } else {
+         NSLog(@"is: %@", title);
+        toolBarTitle.title = title;
+    }
+}
+
+/**
+ * Create a new tool bar button item for use on a previously created tool bar.  Use ::showToolBar to show the new item on the tool bar.
+ *
+ * If the supplied image name is one of the labels listed below, then this method will construct a button
+ * using the standard system buttons.  Note that if you use one of the system images, that the title you supply will be ignored.
+ *
+ * <b>Tool Bar Buttons</b>
+ * UIBarButtonSystemItemDone
+ * UIBarButtonSystemItemCancel
+ * UIBarButtonSystemItemEdit
+ * UIBarButtonSystemItemSave
+ * UIBarButtonSystemItemAdd
+ * UIBarButtonSystemItemFlexibleSpace
+ * UIBarButtonSystemItemFixedSpace
+ * UIBarButtonSystemItemCompose
+ * UIBarButtonSystemItemReply
+ * UIBarButtonSystemItemAction
+ * UIBarButtonSystemItemOrganize
+ * UIBarButtonSystemItemBookmarks
+ * UIBarButtonSystemItemSearch
+ * UIBarButtonSystemItemRefresh
+ * UIBarButtonSystemItemStop
+ * UIBarButtonSystemItemCamera
+ * UIBarButtonSystemItemTrash
+ * UIBarButtonSystemItemPlay
+ * UIBarButtonSystemItemPause
+ * UIBarButtonSystemItemRewind
+ * UIBarButtonSystemItemFastForward
+ * UIBarButtonSystemItemUndo,        // iOS 3.0 and later
+ * UIBarButtonSystemItemRedo,        // iOS 3.0 and later
+ * UIBarButtonSystemItemPageCurl,    // iOS 4.0 and later 
+ * @param {String} name internal name to refer to this tab by
+ * @param {String} [title] title text to show on the button, or null if no text should be shown
+ * @param {String} [image] image filename or internal identifier to show, or null if now image should be shown
+ * @param {Object} [options] Options for customizing the individual tab item [no option available at this time - this is for future proofing]
+ *  
+ */
+- (void)createToolBarItem:(NSArray*)arguments withDict:(NSDictionary*)options
+{
+    if (!toolBar)
+       {
+        [self createToolBar:nil withDict:nil];
+       }
+       
+       if (!toolBarItems)
+       {
+               toolBarItems = [[NSMutableArray alloc] initWithCapacity:1];
+       }
+    
+    NSString  *tagId      = [arguments objectAtIndex:0];
+    NSString  *title     = [arguments objectAtIndex:1];
+       NSString  *imageName;
+       if (arguments.count >= 2)
+       {
+               imageName = [arguments objectAtIndex:2];
+       }
+       
+       NSString  *style;
+       
+       if (arguments.count >= 4)
+       {
+               style    = [arguments objectAtIndex:3];
+       }
+       else 
+       {
+               style = @"UIBarButtonItemStylePlain";
+       }
+    
+       
+       UIBarButtonItemStyle useStyle;
+       
+       if ([style isEqualToString:@"UIBarButtonItemStyleBordered"])
+       {
+               useStyle = UIBarButtonItemStyleBordered;
+       }
+       else if ([style isEqualToString:@"UIBarButtonItemStyleDone"])
+       {
+               useStyle = UIBarButtonItemStyleDone;
+       }
+       else 
+       {
+               useStyle = UIBarButtonItemStylePlain;
+       }
+    
+    UIBarButtonItem *item = nil;    
+    if ([imageName length] > 0) 
+       {
+        UIBarButtonSystemItem systemItem;
+        if ([imageName isEqualToString:@"UIBarButtonSystemItemDone"])
+               {
+                       systemItem = UIBarButtonSystemItemDone;
+               }
+        else if ([imageName isEqualToString:@"UIBarButtonSystemItemCancel"])
+               {
+                       systemItem = UIBarButtonSystemItemCancel;
+               }
+        else if ([imageName isEqualToString:@"UIBarButtonSystemItemEdit"])
+               {
+                       systemItem = UIBarButtonSystemItemEdit;
+               }
+        else if ([imageName isEqualToString:@"UIBarButtonSystemItemSave"])
+               {
+                       systemItem = UIBarButtonSystemItemSave;
+               }
+        else if ([imageName isEqualToString:@"UIBarButtonSystemItemAdd"])
+               {
+                       systemItem = UIBarButtonSystemItemAdd;
+               }
+        else if ([imageName isEqualToString:@"UIBarButtonSystemItemFlexibleSpace"])
+               {
+                       systemItem = UIBarButtonSystemItemFlexibleSpace;
+               }
+        else if ([imageName isEqualToString:@"UIBarButtonSystemItemFixedSpace"])
+               {
+                       systemItem = UIBarButtonSystemItemFixedSpace;
+               }
+        else if ([imageName isEqualToString:@"UIBarButtonSystemItemCompose"])
+               {
+                       systemItem = UIBarButtonSystemItemCompose;
+               }
+        else if ([imageName isEqualToString:@"UIBarButtonSystemItemReply"])
+               {
+                       systemItem = UIBarButtonSystemItemReply;
+               }
+               else if ([imageName isEqualToString:@"UIBarButtonSystemItemAction"])
+               {
+                       systemItem = UIBarButtonSystemItemAction;
+               }
+        else if ([imageName isEqualToString:@"UIBarButtonSystemItemOrganize"])
+               {
+                       systemItem = UIBarButtonSystemItemOrganize;
+               }
+        else if ([imageName isEqualToString:@"UIBarButtonSystemItemBookmarks"])
+               {
+                       systemItem = UIBarButtonSystemItemBookmarks;
+               }
+               else if ([imageName isEqualToString:@"UIBarButtonSystemItemSearch"])
+               {
+                       systemItem = UIBarButtonSystemItemSearch;
+               }
+               else if ([imageName isEqualToString:@"UIBarButtonSystemItemRefresh"])
+               {
+                       systemItem = UIBarButtonSystemItemRefresh;
+               }
+               else if ([imageName isEqualToString:@"UIBarButtonSystemItemStop"])
+               {
+                       systemItem = UIBarButtonSystemItemStop;
+               }
+               else if ([imageName isEqualToString:@"UIBarButtonSystemItemCamera"])
+               {
+                       systemItem = UIBarButtonSystemItemCamera;
+               }
+               else if ([imageName isEqualToString:@"UIBarButtonSystemItemTrash"])
+               {
+                       systemItem = UIBarButtonSystemItemTrash;
+               }
+               else if ([imageName isEqualToString:@"UIBarButtonSystemItemPlay"])
+               {
+                       systemItem = UIBarButtonSystemItemPlay;
+               }
+               else if ([imageName isEqualToString:@"UIBarButtonSystemItemPause"])
+               {
+                       systemItem = UIBarButtonSystemItemPause;
+               }
+               else if ([imageName isEqualToString:@"UIBarButtonSystemItemRewind"])
+               {
+                       systemItem = UIBarButtonSystemItemRewind;
+               }
+               else if ([imageName isEqualToString:@"UIBarButtonSystemItemFastForward"])
+               {
+                       systemItem = UIBarButtonSystemItemFastForward;
+               }
+               else if ([imageName isEqualToString:@"UIBarButtonSystemItemUndo"])
+               {
+                       systemItem = UIBarButtonSystemItemUndo;
+               }
+               else if ([imageName isEqualToString:@"UIBarButtonSystemItemRedo"])
+               {
+                       systemItem = UIBarButtonSystemItemRedo;
+               }
+               else if ([imageName isEqualToString:@"UIBarButtonSystemItemPageCurl"])
+               {
+                       systemItem = UIBarButtonSystemItemPageCurl;
+               }
+        
+               if (systemItem)
+               {
+                       item = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:systemItem target:self action:@selector(toolBarButtonTapped:)];
+                       if ([imageName isEqualToString:@"UIBarButtonSystemItemFixedSpace"])
+                       {
+                               item.width = 14;
+                       }
+               }
+               else
+               {
+                       item = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:imageName] style:useStyle target:self action:@selector(toolBarButtonTapped:)];
+               }
+    }
+       else 
+       {
+               item = [[UIBarButtonItem alloc] initWithTitle:title style:useStyle target:self action:@selector(toolBarButtonTapped:)];
+       }
+    
+       
+    [toolBarItems insertObject:item atIndex:[tagId intValue]];
+       [item release];
+}
+
+- (void)showToolBar:(NSArray*)arguments withDict:(NSDictionary*)options
+{
+    if (!toolBar)
+       {
+        [self createToolBar:nil withDict:nil];
+       }       
+       
+       [toolBar setItems:toolBarItems animated:NO];
+}
+
+- (void) toolBarButtonTapped:(UIBarButtonItem *)button
+{
+       int count = 0;
+    
+       for (UIBarButtonItem* currentButton in toolBarItems)
+       {
+               if (currentButton == button) {
+                       NSString * jsCallBack = [NSString stringWithFormat:@"window.plugins.nativeControls.toolBarButtonTapped(%d);", count];    
+                       [self.webView stringByEvaluatingJavaScriptFromString:jsCallBack];
+                       return;
+               }
+               
+               count++;
+       }
+}
+
+#pragma mark -
+#pragma mark ActionSheet
+
+- (void)createActionSheet:(NSArray*)arguments withDict:(NSDictionary*)options
+{
+    
+       NSString* title = [options objectForKey:@"title"];
+    
+       
+       UIActionSheet* actionSheet = [ [UIActionSheet alloc ] 
+                                  initWithTitle:title 
+                                  delegate:self 
+                                  cancelButtonTitle:nil 
+                                  destructiveButtonTitle:nil
+                                  otherButtonTitles:nil
+                                  ];
+       
+       int count = [arguments count];
+       for(int n = 0; n < count; n++)
+       {
+               [ actionSheet addButtonWithTitle:[arguments objectAtIndex:n]];
+       }
+       
+       if([options objectForKey:@"cancelButtonIndex"])
+       {
+               actionSheet.cancelButtonIndex = [[options objectForKey:@"cancelButtonIndex"] intValue];
+       }
+       if([options objectForKey:@"destructiveButtonIndex"])
+       {
+               actionSheet.destructiveButtonIndex = [[options objectForKey:@"destructiveButtonIndex"] intValue];
+       }
+       
+       actionSheet.actionSheetStyle = UIActionSheetStyleBlackTranslucent;//UIActionSheetStyleBlackOpaque;
+    [actionSheet showInView:self.webView.superview];
+    [actionSheet release];
+       
+}
+
+
+- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
+{
+       NSString * jsCallBack = [NSString stringWithFormat:@"window.plugins.nativeControls._onActionSheetDismissed(%d);", buttonIndex];    
+    [self.webView stringByEvaluatingJavaScriptFromString:jsCallBack];
+}
+
+
+
+@end
\ No newline at end of file
diff --git a/Plugins/README b/Plugins/README
new file mode 100644 (file)
index 0000000..438840d
--- /dev/null
@@ -0,0 +1 @@
+Put the .h and .m files of your plugin here. The .js files of your plugin belong in the www folder.
\ No newline at end of file
diff --git a/Resources/icons/icon-72.png b/Resources/icons/icon-72.png
new file mode 100755 (executable)
index 0000000..9db5b55
Binary files /dev/null and b/Resources/icons/icon-72.png differ
diff --git a/Resources/icons/icon.png b/Resources/icons/icon.png
new file mode 100755 (executable)
index 0000000..2566dcb
Binary files /dev/null and b/Resources/icons/icon.png differ
diff --git a/Resources/icons/icon@2x.png b/Resources/icons/icon@2x.png
new file mode 100755 (executable)
index 0000000..37328fe
Binary files /dev/null and b/Resources/icons/icon@2x.png differ
diff --git a/Resources/splash/Default-Landscape.png b/Resources/splash/Default-Landscape.png
new file mode 100755 (executable)
index 0000000..897e098
Binary files /dev/null and b/Resources/splash/Default-Landscape.png differ
diff --git a/Resources/splash/Default-Portrait.png b/Resources/splash/Default-Portrait.png
new file mode 100755 (executable)
index 0000000..42cf2a6
Binary files /dev/null and b/Resources/splash/Default-Portrait.png differ
diff --git a/Resources/splash/Default.png b/Resources/splash/Default.png
new file mode 100755 (executable)
index 0000000..da87cce
Binary files /dev/null and b/Resources/splash/Default.png differ
diff --git a/Resources/splash/Default@2x.png b/Resources/splash/Default@2x.png
new file mode 100755 (executable)
index 0000000..15db6a4
Binary files /dev/null and b/Resources/splash/Default@2x.png differ
diff --git a/Wolne Lektury b/Wolne Lektury
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Wolne Lektury.xcodeproj/fnp.mode1v3 b/Wolne Lektury.xcodeproj/fnp.mode1v3
new file mode 100644 (file)
index 0000000..62e5fbd
--- /dev/null
@@ -0,0 +1,1990 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>ActivePerspectiveName</key>
+       <string>Project</string>
+       <key>AllowedModules</key>
+       <array>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>PBXSmartGroupTreeModule</string>
+                       <key>Name</key>
+                       <string>Groups and Files Outline View</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>PBXNavigatorGroup</string>
+                       <key>Name</key>
+                       <string>Editor</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>XCTaskListModule</string>
+                       <key>Name</key>
+                       <string>Task List</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>XCDetailModule</string>
+                       <key>Name</key>
+                       <string>File and Smart Group Detail Viewer</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>1</string>
+                       <key>Module</key>
+                       <string>PBXBuildResultsModule</string>
+                       <key>Name</key>
+                       <string>Detailed Build Results Viewer</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>1</string>
+                       <key>Module</key>
+                       <string>PBXProjectFindModule</string>
+                       <key>Name</key>
+                       <string>Project Batch Find Tool</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>XCProjectFormatConflictsModule</string>
+                       <key>Name</key>
+                       <string>Project Format Conflicts List</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>PBXBookmarksModule</string>
+                       <key>Name</key>
+                       <string>Bookmarks Tool</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>PBXClassBrowserModule</string>
+                       <key>Name</key>
+                       <string>Class Browser</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>PBXCVSModule</string>
+                       <key>Name</key>
+                       <string>Source Code Control Tool</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>PBXDebugBreakpointsModule</string>
+                       <key>Name</key>
+                       <string>Debug Breakpoints Tool</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>XCDockableInspector</string>
+                       <key>Name</key>
+                       <string>Inspector</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>PBXOpenQuicklyModule</string>
+                       <key>Name</key>
+                       <string>Open Quickly Tool</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>1</string>
+                       <key>Module</key>
+                       <string>PBXDebugSessionModule</string>
+                       <key>Name</key>
+                       <string>Debugger</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>1</string>
+                       <key>Module</key>
+                       <string>PBXDebugCLIModule</string>
+                       <key>Name</key>
+                       <string>Debug Console</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>XCSnapshotModule</string>
+                       <key>Name</key>
+                       <string>Snapshots Tool</string>
+               </dict>
+       </array>
+       <key>BundlePath</key>
+       <string>/Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources</string>
+       <key>Description</key>
+       <string>DefaultDescriptionKey</string>
+       <key>DockingSystemVisible</key>
+       <false/>
+       <key>Extension</key>
+       <string>mode1v3</string>
+       <key>FavBarConfig</key>
+       <dict>
+               <key>PBXProjectModuleGUID</key>
+               <string>98DF31FE144EDB8D001381F0</string>
+               <key>XCBarModuleItemNames</key>
+               <dict/>
+               <key>XCBarModuleItems</key>
+               <array/>
+       </dict>
+       <key>FirstTimeWindowDisplayed</key>
+       <false/>
+       <key>Identifier</key>
+       <string>com.apple.perspectives.project.mode1v3</string>
+       <key>MajorVersion</key>
+       <integer>33</integer>
+       <key>MinorVersion</key>
+       <integer>0</integer>
+       <key>Name</key>
+       <string>Default</string>
+       <key>Notifications</key>
+       <array/>
+       <key>OpenEditors</key>
+       <array>
+               <dict>
+                       <key>Content</key>
+                       <dict>
+                               <key>PBXProjectModuleGUID</key>
+                               <string>98E0622E145879DF008FCAF3</string>
+                               <key>PBXProjectModuleLabel</key>
+                               <string>style.css</string>
+                               <key>PBXSplitModuleInNavigatorKey</key>
+                               <dict>
+                                       <key>Split0</key>
+                                       <dict>
+                                               <key>PBXProjectModuleGUID</key>
+                                               <string>98E0622F145879DF008FCAF3</string>
+                                               <key>PBXProjectModuleLabel</key>
+                                               <string>style.css</string>
+                                               <key>_historyCapacity</key>
+                                               <integer>0</integer>
+                                               <key>bookmark</key>
+                                               <string>984B12AA14AB6F46004BF082</string>
+                                               <key>history</key>
+                                               <array>
+                                                       <string>984B103914AB4E96004BF082</string>
+                                               </array>
+                                       </dict>
+                                       <key>SplitCount</key>
+                                       <string>1</string>
+                               </dict>
+                               <key>StatusBarVisibility</key>
+                               <true/>
+                       </dict>
+                       <key>Geometry</key>
+                       <dict>
+                               <key>Frame</key>
+                               <string>{{0, 20}, {658, 576}}</string>
+                               <key>PBXModuleWindowStatusBarHidden2</key>
+                               <false/>
+                               <key>RubberWindowFrame</key>
+                               <string>622 161 658 617 0 0 1280 778 </string>
+                       </dict>
+               </dict>
+               <dict>
+                       <key>Content</key>
+                       <dict>
+                               <key>PBXProjectModuleGUID</key>
+                               <string>98B7EBAE1491163B000EE462</string>
+                               <key>PBXProjectModuleLabel</key>
+                               <string>view.js</string>
+                               <key>PBXSplitModuleInNavigatorKey</key>
+                               <dict>
+                                       <key>Split0</key>
+                                       <dict>
+                                               <key>PBXProjectModuleGUID</key>
+                                               <string>98B7EBAF1491163B000EE462</string>
+                                               <key>PBXProjectModuleLabel</key>
+                                               <string>view.js</string>
+                                               <key>_historyCapacity</key>
+                                               <integer>0</integer>
+                                               <key>bookmark</key>
+                                               <string>984B12AC14AB6F46004BF082</string>
+                                               <key>history</key>
+                                               <array>
+                                                       <string>984B103C14AB4E96004BF082</string>
+                                               </array>
+                                       </dict>
+                                       <key>SplitCount</key>
+                                       <string>1</string>
+                               </dict>
+                               <key>StatusBarVisibility</key>
+                               <true/>
+                       </dict>
+                       <key>Geometry</key>
+                       <dict>
+                               <key>Frame</key>
+                               <string>{{0, 20}, {658, 576}}</string>
+                               <key>PBXModuleWindowStatusBarHidden2</key>
+                               <false/>
+                               <key>RubberWindowFrame</key>
+                               <string>223 118 658 617 0 0 1280 778 </string>
+                       </dict>
+               </dict>
+               <dict>
+                       <key>Content</key>
+                       <dict>
+                               <key>PBXProjectModuleGUID</key>
+                               <string>984B10EC14AB6716004BF082</string>
+                               <key>PBXProjectModuleLabel</key>
+                               <string>menu.js</string>
+                               <key>PBXSplitModuleInNavigatorKey</key>
+                               <dict>
+                                       <key>Split0</key>
+                                       <dict>
+                                               <key>PBXProjectModuleGUID</key>
+                                               <string>984B10ED14AB6716004BF082</string>
+                                               <key>PBXProjectModuleLabel</key>
+                                               <string>menu.js</string>
+                                               <key>_historyCapacity</key>
+                                               <integer>0</integer>
+                                               <key>bookmark</key>
+                                               <string>984B12AE14AB6F46004BF082</string>
+                                               <key>history</key>
+                                               <array>
+                                                       <string>984B10EE14AB6716004BF082</string>
+                                               </array>
+                                       </dict>
+                                       <key>SplitCount</key>
+                                       <string>1</string>
+                               </dict>
+                               <key>StatusBarVisibility</key>
+                               <true/>
+                       </dict>
+                       <key>Geometry</key>
+                       <dict>
+                               <key>Frame</key>
+                               <string>{{0, 20}, {658, 576}}</string>
+                               <key>PBXModuleWindowStatusBarHidden2</key>
+                               <false/>
+                               <key>RubberWindowFrame</key>
+                               <string>607 161 658 617 0 0 1280 778 </string>
+                       </dict>
+               </dict>
+               <dict>
+                       <key>Content</key>
+                       <dict>
+                               <key>PBXProjectModuleGUID</key>
+                               <string>984B103E14AB4E96004BF082</string>
+                               <key>PBXProjectModuleLabel</key>
+                               <string>main.js</string>
+                               <key>PBXSplitModuleInNavigatorKey</key>
+                               <dict>
+                                       <key>Split0</key>
+                                       <dict>
+                                               <key>PBXProjectModuleGUID</key>
+                                               <string>984B103F14AB4E96004BF082</string>
+                                               <key>PBXProjectModuleLabel</key>
+                                               <string>main.js</string>
+                                               <key>_historyCapacity</key>
+                                               <integer>0</integer>
+                                               <key>bookmark</key>
+                                               <string>984B12B014AB6F46004BF082</string>
+                                               <key>history</key>
+                                               <array>
+                                                       <string>984B104014AB4E96004BF082</string>
+                                               </array>
+                                       </dict>
+                                       <key>SplitCount</key>
+                                       <string>1</string>
+                               </dict>
+                               <key>StatusBarVisibility</key>
+                               <true/>
+                       </dict>
+                       <key>Geometry</key>
+                       <dict>
+                               <key>Frame</key>
+                               <string>{{0, 20}, {658, 576}}</string>
+                               <key>PBXModuleWindowStatusBarHidden2</key>
+                               <false/>
+                               <key>RubberWindowFrame</key>
+                               <string>354 148 658 617 0 0 1280 778 </string>
+                       </dict>
+               </dict>
+               <dict>
+                       <key>Content</key>
+                       <dict>
+                               <key>PBXProjectModuleGUID</key>
+                               <string>98E0622A145879DF008FCAF3</string>
+                               <key>PBXProjectModuleLabel</key>
+                               <string>book_text.css</string>
+                               <key>PBXSplitModuleInNavigatorKey</key>
+                               <dict>
+                                       <key>Split0</key>
+                                       <dict>
+                                               <key>PBXProjectModuleGUID</key>
+                                               <string>98E0622B145879DF008FCAF3</string>
+                                               <key>PBXProjectModuleLabel</key>
+                                               <string>book_text.css</string>
+                                               <key>_historyCapacity</key>
+                                               <integer>0</integer>
+                                               <key>bookmark</key>
+                                               <string>984B12B214AB6F46004BF082</string>
+                                               <key>history</key>
+                                               <array>
+                                                       <string>984B103614AB4E96004BF082</string>
+                                               </array>
+                                       </dict>
+                                       <key>SplitCount</key>
+                                       <string>1</string>
+                               </dict>
+                               <key>StatusBarVisibility</key>
+                               <true/>
+                       </dict>
+                       <key>Geometry</key>
+                       <dict>
+                               <key>Frame</key>
+                               <string>{{0, 20}, {658, 576}}</string>
+                               <key>PBXModuleWindowStatusBarHidden2</key>
+                               <false/>
+                               <key>RubberWindowFrame</key>
+                               <string>575 156 658 617 0 0 1280 778 </string>
+                       </dict>
+               </dict>
+               <dict>
+                       <key>Content</key>
+                       <dict>
+                               <key>PBXProjectModuleGUID</key>
+                               <string>984B104214AB4E96004BF082</string>
+                               <key>PBXProjectModuleLabel</key>
+                               <string>PhoneGap.plist</string>
+                               <key>PBXSplitModuleInNavigatorKey</key>
+                               <dict>
+                                       <key>Split0</key>
+                                       <dict>
+                                               <key>PBXProjectModuleGUID</key>
+                                               <string>984B104314AB4E96004BF082</string>
+                                               <key>PBXProjectModuleLabel</key>
+                                               <string>PhoneGap.plist</string>
+                                               <key>_historyCapacity</key>
+                                               <integer>0</integer>
+                                               <key>bookmark</key>
+                                               <string>984B12B414AB6F46004BF082</string>
+                                               <key>history</key>
+                                               <array>
+                                                       <string>984B104514AB4E96004BF082</string>
+                                               </array>
+                                       </dict>
+                                       <key>SplitCount</key>
+                                       <string>1</string>
+                               </dict>
+                               <key>StatusBarVisibility</key>
+                               <true/>
+                       </dict>
+                       <key>Geometry</key>
+                       <dict>
+                               <key>Frame</key>
+                               <string>{{0, 20}, {750, 461}}</string>
+                               <key>PBXModuleWindowStatusBarHidden2</key>
+                               <false/>
+                               <key>RubberWindowFrame</key>
+                               <string>61 228 750 502 0 0 1280 778 </string>
+                       </dict>
+               </dict>
+               <dict>
+                       <key>Content</key>
+                       <dict>
+                               <key>PBXProjectModuleGUID</key>
+                               <string>98B602FA144F0D5200E746E6</string>
+                               <key>PBXProjectModuleLabel</key>
+                               <string>AppDelegate.h</string>
+                               <key>PBXSplitModuleInNavigatorKey</key>
+                               <dict>
+                                       <key>Split0</key>
+                                       <dict>
+                                               <key>PBXProjectModuleGUID</key>
+                                               <string>98B602FB144F0D5200E746E6</string>
+                                               <key>PBXProjectModuleLabel</key>
+                                               <string>AppDelegate.h</string>
+                                               <key>_historyCapacity</key>
+                                               <integer>0</integer>
+                                               <key>bookmark</key>
+                                               <string>984B12B514AB6F46004BF082</string>
+                                               <key>history</key>
+                                               <array>
+                                                       <string>984B104614AB4E96004BF082</string>
+                                               </array>
+                                       </dict>
+                                       <key>SplitCount</key>
+                                       <string>1</string>
+                               </dict>
+                               <key>StatusBarVisibility</key>
+                               <true/>
+                       </dict>
+                       <key>Geometry</key>
+                       <dict>
+                               <key>Frame</key>
+                               <string>{{0, 20}, {750, 461}}</string>
+                               <key>PBXModuleWindowStatusBarHidden2</key>
+                               <false/>
+                               <key>RubberWindowFrame</key>
+                               <string>15 271 750 502 0 0 1280 778 </string>
+                       </dict>
+               </dict>
+               <dict>
+                       <key>Content</key>
+                       <dict>
+                               <key>PBXProjectModuleGUID</key>
+                               <string>98DF3515144F0095001381F0</string>
+                               <key>PBXProjectModuleLabel</key>
+                               <string>main.m</string>
+                               <key>PBXSplitModuleInNavigatorKey</key>
+                               <dict>
+                                       <key>Split0</key>
+                                       <dict>
+                                               <key>PBXProjectModuleGUID</key>
+                                               <string>98DF3516144F0095001381F0</string>
+                                               <key>PBXProjectModuleLabel</key>
+                                               <string>main.m</string>
+                                               <key>_historyCapacity</key>
+                                               <integer>0</integer>
+                                               <key>bookmark</key>
+                                               <string>984B12B614AB6F46004BF082</string>
+                                               <key>history</key>
+                                               <array>
+                                                       <string>984B104714AB4E96004BF082</string>
+                                               </array>
+                                       </dict>
+                                       <key>SplitCount</key>
+                                       <string>1</string>
+                               </dict>
+                               <key>StatusBarVisibility</key>
+                               <true/>
+                       </dict>
+                       <key>Geometry</key>
+                       <dict>
+                               <key>Frame</key>
+                               <string>{{0, 20}, {750, 461}}</string>
+                               <key>PBXModuleWindowStatusBarHidden2</key>
+                               <false/>
+                               <key>RubberWindowFrame</key>
+                               <string>107 187 750 502 0 0 1280 778 </string>
+                       </dict>
+               </dict>
+               <dict>
+                       <key>Content</key>
+                       <dict>
+                               <key>PBXProjectModuleGUID</key>
+                               <string>98DF3512144F0095001381F0</string>
+                               <key>PBXProjectModuleLabel</key>
+                               <string>AppDelegate.m</string>
+                               <key>PBXSplitModuleInNavigatorKey</key>
+                               <dict>
+                                       <key>Split0</key>
+                                       <dict>
+                                               <key>PBXProjectModuleGUID</key>
+                                               <string>98DF3513144F0095001381F0</string>
+                                               <key>PBXProjectModuleLabel</key>
+                                               <string>AppDelegate.m</string>
+                                               <key>_historyCapacity</key>
+                                               <integer>0</integer>
+                                               <key>bookmark</key>
+                                               <string>984B12B714AB6F46004BF082</string>
+                                               <key>history</key>
+                                               <array>
+                                                       <string>984B104814AB4E96004BF082</string>
+                                               </array>
+                                       </dict>
+                                       <key>SplitCount</key>
+                                       <string>1</string>
+                               </dict>
+                               <key>StatusBarVisibility</key>
+                               <true/>
+                       </dict>
+                       <key>Geometry</key>
+                       <dict>
+                               <key>Frame</key>
+                               <string>{{0, 20}, {750, 461}}</string>
+                               <key>PBXModuleWindowStatusBarHidden2</key>
+                               <false/>
+                               <key>RubberWindowFrame</key>
+                               <string>130 166 750 502 0 0 1280 778 </string>
+                       </dict>
+               </dict>
+               <dict>
+                       <key>Content</key>
+                       <dict>
+                               <key>PBXProjectModuleGUID</key>
+                               <string>984B104914AB4E96004BF082</string>
+                               <key>PBXProjectModuleLabel</key>
+                               <string>filerepo.js</string>
+                               <key>PBXSplitModuleInNavigatorKey</key>
+                               <dict>
+                                       <key>Split0</key>
+                                       <dict>
+                                               <key>PBXProjectModuleGUID</key>
+                                               <string>984B104A14AB4E96004BF082</string>
+                                               <key>PBXProjectModuleLabel</key>
+                                               <string>filerepo.js</string>
+                                               <key>_historyCapacity</key>
+                                               <integer>0</integer>
+                                               <key>bookmark</key>
+                                               <string>984B12B814AB6F46004BF082</string>
+                                               <key>history</key>
+                                               <array>
+                                                       <string>984B104B14AB4E96004BF082</string>
+                                               </array>
+                                       </dict>
+                                       <key>SplitCount</key>
+                                       <string>1</string>
+                               </dict>
+                               <key>StatusBarVisibility</key>
+                               <true/>
+                       </dict>
+                       <key>Geometry</key>
+                       <dict>
+                               <key>Frame</key>
+                               <string>{{0, 20}, {658, 576}}</string>
+                               <key>PBXModuleWindowStatusBarHidden2</key>
+                               <false/>
+                               <key>RubberWindowFrame</key>
+                               <string>394 150 658 617 0 0 1280 778 </string>
+                       </dict>
+               </dict>
+               <dict>
+                       <key>Content</key>
+                       <dict>
+                               <key>PBXProjectModuleGUID</key>
+                               <string>98DF3409144EE6D2001381F0</string>
+                               <key>PBXProjectModuleLabel</key>
+                               <string>catalogue.js</string>
+                               <key>PBXSplitModuleInNavigatorKey</key>
+                               <dict>
+                                       <key>Split0</key>
+                                       <dict>
+                                               <key>PBXProjectModuleGUID</key>
+                                               <string>98DF340A144EE6D2001381F0</string>
+                                               <key>PBXProjectModuleLabel</key>
+                                               <string>catalogue.js</string>
+                                               <key>_historyCapacity</key>
+                                               <integer>0</integer>
+                                               <key>bookmark</key>
+                                               <string>984B12B914AB6F46004BF082</string>
+                                               <key>history</key>
+                                               <array>
+                                                       <string>984B104D14AB4E96004BF082</string>
+                                               </array>
+                                       </dict>
+                                       <key>SplitCount</key>
+                                       <string>1</string>
+                               </dict>
+                               <key>StatusBarVisibility</key>
+                               <true/>
+                       </dict>
+                       <key>Geometry</key>
+                       <dict>
+                               <key>Frame</key>
+                               <string>{{0, 20}, {750, 461}}</string>
+                               <key>PBXModuleWindowStatusBarHidden2</key>
+                               <false/>
+                               <key>RubberWindowFrame</key>
+                               <string>273 224 750 502 0 0 1280 778 </string>
+                       </dict>
+               </dict>
+               <dict>
+                       <key>Content</key>
+                       <dict>
+                               <key>PBXProjectModuleGUID</key>
+                               <string>984B104E14AB4E96004BF082</string>
+                               <key>PBXProjectModuleLabel</key>
+                               <string>history.js</string>
+                               <key>PBXSplitModuleInNavigatorKey</key>
+                               <dict>
+                                       <key>Split0</key>
+                                       <dict>
+                                               <key>PBXProjectModuleGUID</key>
+                                               <string>984B104F14AB4E96004BF082</string>
+                                               <key>PBXProjectModuleLabel</key>
+                                               <string>history.js</string>
+                                               <key>_historyCapacity</key>
+                                               <integer>0</integer>
+                                               <key>bookmark</key>
+                                               <string>984B12BA14AB6F46004BF082</string>
+                                               <key>history</key>
+                                               <array>
+                                                       <string>984B105014AB4E96004BF082</string>
+                                               </array>
+                                       </dict>
+                                       <key>SplitCount</key>
+                                       <string>1</string>
+                               </dict>
+                               <key>StatusBarVisibility</key>
+                               <true/>
+                       </dict>
+                       <key>Geometry</key>
+                       <dict>
+                               <key>Frame</key>
+                               <string>{{0, 20}, {658, 576}}</string>
+                               <key>PBXModuleWindowStatusBarHidden2</key>
+                               <false/>
+                               <key>RubberWindowFrame</key>
+                               <string>471 147 658 617 0 0 1280 778 </string>
+                       </dict>
+               </dict>
+               <dict>
+                       <key>Content</key>
+                       <dict>
+                               <key>PBXProjectModuleGUID</key>
+                               <string>98DF3405144EE6D2001381F0</string>
+                               <key>PBXProjectModuleLabel</key>
+                               <string>dbput.js</string>
+                               <key>PBXSplitModuleInNavigatorKey</key>
+                               <dict>
+                                       <key>Split0</key>
+                                       <dict>
+                                               <key>PBXProjectModuleGUID</key>
+                                               <string>98DF3406144EE6D2001381F0</string>
+                                               <key>PBXProjectModuleLabel</key>
+                                               <string>dbput.js</string>
+                                               <key>_historyCapacity</key>
+                                               <integer>0</integer>
+                                               <key>bookmark</key>
+                                               <string>984B12BC14AB6F46004BF082</string>
+                                               <key>history</key>
+                                               <array>
+                                                       <string>984B105214AB4E96004BF082</string>
+                                               </array>
+                                       </dict>
+                                       <key>SplitCount</key>
+                                       <string>1</string>
+                               </dict>
+                               <key>StatusBarVisibility</key>
+                               <true/>
+                       </dict>
+                       <key>Geometry</key>
+                       <dict>
+                               <key>Frame</key>
+                               <string>{{0, 20}, {750, 461}}</string>
+                               <key>PBXModuleWindowStatusBarHidden2</key>
+                               <false/>
+                               <key>RubberWindowFrame</key>
+                               <string>61 229 750 502 0 0 1280 778 </string>
+                       </dict>
+               </dict>
+       </array>
+       <key>PerspectiveWidths</key>
+       <array>
+               <integer>-1</integer>
+               <integer>-1</integer>
+       </array>
+       <key>Perspectives</key>
+       <array>
+               <dict>
+                       <key>ChosenToolbarItems</key>
+                       <array>
+                               <string>active-combo-popup</string>
+                               <string>action</string>
+                               <string>NSToolbarFlexibleSpaceItem</string>
+                               <string>debugger-enable-breakpoints</string>
+                               <string>build-and-go</string>
+                               <string>com.apple.ide.PBXToolbarStopButton</string>
+                               <string>get-info</string>
+                               <string>NSToolbarFlexibleSpaceItem</string>
+                               <string>com.apple.pbx.toolbar.searchfield</string>
+                       </array>
+                       <key>ControllerClassBaseName</key>
+                       <string></string>
+                       <key>IconName</key>
+                       <string>WindowOfProjectWithEditor</string>
+                       <key>Identifier</key>
+                       <string>perspective.project</string>
+                       <key>IsVertical</key>
+                       <false/>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>BecomeActive</key>
+                                       <true/>
+                                       <key>ContentConfiguration</key>
+                                       <dict>
+                                               <key>PBXBottomSmartGroupGIDs</key>
+                                               <array>
+                                                       <string>1C37FBAC04509CD000000102</string>
+                                                       <string>1C37FAAC04509CD000000102</string>
+                                                       <string>1C37FABC05509CD000000102</string>
+                                                       <string>1C37FABC05539CD112110102</string>
+                                                       <string>E2644B35053B69B200211256</string>
+                                                       <string>1C37FABC04509CD000100104</string>
+                                                       <string>1CC0EA4004350EF90044410B</string>
+                                                       <string>1CC0EA4004350EF90041110B</string>
+                                               </array>
+                                               <key>PBXProjectModuleGUID</key>
+                                               <string>1CE0B1FE06471DED0097A5F4</string>
+                                               <key>PBXProjectModuleLabel</key>
+                                               <string>Files</string>
+                                               <key>PBXProjectStructureProvided</key>
+                                               <string>yes</string>
+                                               <key>PBXSmartGroupTreeModuleColumnData</key>
+                                               <dict>
+                                                       <key>PBXSmartGroupTreeModuleColumnWidthsKey</key>
+                                                       <array>
+                                                               <real>186</real>
+                                                       </array>
+                                                       <key>PBXSmartGroupTreeModuleColumnsKey_v4</key>
+                                                       <array>
+                                                               <string>MainColumn</string>
+                                                       </array>
+                                               </dict>
+                                               <key>PBXSmartGroupTreeModuleOutlineStateKey_v7</key>
+                                               <dict>
+                                                       <key>PBXSmartGroupTreeModuleOutlineStateExpansionKey</key>
+                                                       <array>
+                                                               <string>29B97314FDCFA39411CA2CEA</string>
+                                                               <string>98DF335B144EE40D001381F0</string>
+                                                               <string>984B0F4214AB25C9004BF082</string>
+                                                               <string>984B0F4314AB25C9004BF082</string>
+                                                               <string>301BF52D109A57CC0062928A</string>
+                                                               <string>080E96DDFE201D6D7F000001</string>
+                                                               <string>307C750510C5A3420062BCA9</string>
+                                                               <string>98B602E2144F0C4200E746E6</string>
+                                                               <string>29B97315FDCFA39411CA2CEA</string>
+                                                               <string>29B97317FDCFA39411CA2CEA</string>
+                                                               <string>308D052D1370CCF300D202BF</string>
+                                                               <string>308D05311370CCF300D202BF</string>
+                                                               <string>29B97323FDCFA39411CA2CEA</string>
+                                                               <string>19C28FACFE9D520D11CA2CBB</string>
+                                                               <string>1C37FBAC04509CD000000102</string>
+                                                               <string>1C37FAAC04509CD000000102</string>
+                                                               <string>1C37FABC05509CD000000102</string>
+                                                       </array>
+                                                       <key>PBXSmartGroupTreeModuleOutlineStateSelectionKey</key>
+                                                       <array>
+                                                               <array>
+                                                                       <integer>3</integer>
+                                                                       <integer>1</integer>
+                                                                       <integer>0</integer>
+                                                               </array>
+                                                       </array>
+                                                       <key>PBXSmartGroupTreeModuleOutlineStateVisibleRectKey</key>
+                                                       <string>{{0, 0}, {186, 573}}</string>
+                                               </dict>
+                                               <key>PBXTopSmartGroupGIDs</key>
+                                               <array/>
+                                               <key>XCIncludePerspectivesSwitch</key>
+                                               <true/>
+                                               <key>XCSharingToken</key>
+                                               <string>com.apple.Xcode.GFSharingToken</string>
+                                       </dict>
+                                       <key>GeometryConfiguration</key>
+                                       <dict>
+                                               <key>Frame</key>
+                                               <string>{{0, 0}, {203, 591}}</string>
+                                               <key>GroupTreeTableConfiguration</key>
+                                               <array>
+                                                       <string>MainColumn</string>
+                                                       <real>186</real>
+                                               </array>
+                                               <key>RubberWindowFrame</key>
+                                               <string>-1 146 1280 632 0 0 1280 778 </string>
+                                       </dict>
+                                       <key>Module</key>
+                                       <string>PBXSmartGroupTreeModule</string>
+                                       <key>Proportion</key>
+                                       <string>203pt</string>
+                               </dict>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1CE0B20306471E060097A5F4</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>WolneLektury</string>
+                                                               <key>PBXSplitModuleInNavigatorKey</key>
+                                                               <dict>
+                                                                       <key>Split0</key>
+                                                                       <dict>
+                                                                               <key>PBXProjectModuleGUID</key>
+                                                                               <string>1CE0B20406471E060097A5F4</string>
+                                                                               <key>PBXProjectModuleLabel</key>
+                                                                               <string>WolneLektury</string>
+                                                                               <key>_historyCapacity</key>
+                                                                               <integer>0</integer>
+                                                                               <key>bookmark</key>
+                                                                               <string>984B12A814AB6F46004BF082</string>
+                                                                               <key>history</key>
+                                                                               <array>
+                                                                                       <string>98DF33E2144EE6D2001381F0</string>
+                                                                                       <string>98B7EA83149103F1000EE462</string>
+                                                                                       <string>98B7EAFD14910675000EE462</string>
+                                                                                       <string>98B7EAFE14910675000EE462</string>
+                                                                                       <string>98B7EB0314910675000EE462</string>
+                                                                                       <string>98B7EB0414910675000EE462</string>
+                                                                                       <string>98B7EB0514910675000EE462</string>
+                                                                                       <string>98B7EB0614910675000EE462</string>
+                                                                                       <string>98B7EB0714910675000EE462</string>
+                                                                                       <string>98B7EB0814910675000EE462</string>
+                                                                                       <string>98B7EB0914910675000EE462</string>
+                                                                                       <string>98B7EB8914911229000EE462</string>
+                                                                                       <string>98B7EBA31491163B000EE462</string>
+                                                                                       <string>98B7EBA41491163B000EE462</string>
+                                                                                       <string>98B7EBA61491163B000EE462</string>
+                                                                                       <string>98B7EBA71491163B000EE462</string>
+                                                                                       <string>98B7EBA81491163B000EE462</string>
+                                                                                       <string>98B7EBA91491163B000EE462</string>
+                                                                                       <string>984B102614AB4E96004BF082</string>
+                                                                                       <string>984B102714AB4E96004BF082</string>
+                                                                                       <string>984B102814AB4E96004BF082</string>
+                                                                                       <string>984B102914AB4E96004BF082</string>
+                                                                                       <string>984B102A14AB4E96004BF082</string>
+                                                                                       <string>984B102B14AB4E96004BF082</string>
+                                                                                       <string>984B102C14AB4E96004BF082</string>
+                                                                                       <string>984B102D14AB4E96004BF082</string>
+                                                                                       <string>984B102E14AB4E96004BF082</string>
+                                                                                       <string>984B103014AB4E96004BF082</string>
+                                                                                       <string>984B106C14AB4EFB004BF082</string>
+                                                                               </array>
+                                                                       </dict>
+                                                                       <key>SplitCount</key>
+                                                                       <string>1</string>
+                                                               </dict>
+                                                               <key>StatusBarVisibility</key>
+                                                               <true/>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 0}, {1072, 363}}</string>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>-1 146 1280 632 0 0 1280 778 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>PBXNavigatorGroup</string>
+                                                       <key>Proportion</key>
+                                                       <string>363pt</string>
+                                               </dict>
+                                               <dict>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1CE0B20506471E060097A5F4</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>Detail</string>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 368}, {1072, 223}}</string>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>-1 146 1280 632 0 0 1280 778 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>XCDetailModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>223pt</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>1072pt</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Project</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>XCModuleDock</string>
+                               <string>PBXSmartGroupTreeModule</string>
+                               <string>XCModuleDock</string>
+                               <string>PBXNavigatorGroup</string>
+                               <string>XCDetailModule</string>
+                       </array>
+                       <key>TableOfContents</key>
+                       <array>
+                               <string>984B106E14AB4EFB004BF082</string>
+                               <string>1CE0B1FE06471DED0097A5F4</string>
+                               <string>984B106F14AB4EFB004BF082</string>
+                               <string>1CE0B20306471E060097A5F4</string>
+                               <string>1CE0B20506471E060097A5F4</string>
+                       </array>
+                       <key>ToolbarConfigUserDefaultsMinorVersion</key>
+                       <string>2</string>
+                       <key>ToolbarConfiguration</key>
+                       <string>xcode.toolbar.config.defaultV3</string>
+               </dict>
+               <dict>
+                       <key>ControllerClassBaseName</key>
+                       <string></string>
+                       <key>IconName</key>
+                       <string>WindowOfProject</string>
+                       <key>Identifier</key>
+                       <string>perspective.morph</string>
+                       <key>IsVertical</key>
+                       <integer>0</integer>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>BecomeActive</key>
+                                       <integer>1</integer>
+                                       <key>ContentConfiguration</key>
+                                       <dict>
+                                               <key>PBXBottomSmartGroupGIDs</key>
+                                               <array>
+                                                       <string>1C37FBAC04509CD000000102</string>
+                                                       <string>1C37FAAC04509CD000000102</string>
+                                                       <string>1C08E77C0454961000C914BD</string>
+                                                       <string>1C37FABC05509CD000000102</string>
+                                                       <string>1C37FABC05539CD112110102</string>
+                                                       <string>E2644B35053B69B200211256</string>
+                                                       <string>1C37FABC04509CD000100104</string>
+                                                       <string>1CC0EA4004350EF90044410B</string>
+                                                       <string>1CC0EA4004350EF90041110B</string>
+                                               </array>
+                                               <key>PBXProjectModuleGUID</key>
+                                               <string>11E0B1FE06471DED0097A5F4</string>
+                                               <key>PBXProjectModuleLabel</key>
+                                               <string>Files</string>
+                                               <key>PBXProjectStructureProvided</key>
+                                               <string>yes</string>
+                                               <key>PBXSmartGroupTreeModuleColumnData</key>
+                                               <dict>
+                                                       <key>PBXSmartGroupTreeModuleColumnWidthsKey</key>
+                                                       <array>
+                                                               <real>186</real>
+                                                       </array>
+                                                       <key>PBXSmartGroupTreeModuleColumnsKey_v4</key>
+                                                       <array>
+                                                               <string>MainColumn</string>
+                                                       </array>
+                                               </dict>
+                                               <key>PBXSmartGroupTreeModuleOutlineStateKey_v7</key>
+                                               <dict>
+                                                       <key>PBXSmartGroupTreeModuleOutlineStateExpansionKey</key>
+                                                       <array>
+                                                               <string>29B97314FDCFA39411CA2CEA</string>
+                                                               <string>1C37FABC05509CD000000102</string>
+                                                       </array>
+                                                       <key>PBXSmartGroupTreeModuleOutlineStateSelectionKey</key>
+                                                       <array>
+                                                               <array>
+                                                                       <integer>0</integer>
+                                                               </array>
+                                                       </array>
+                                                       <key>PBXSmartGroupTreeModuleOutlineStateVisibleRectKey</key>
+                                                       <string>{{0, 0}, {186, 337}}</string>
+                                               </dict>
+                                               <key>PBXTopSmartGroupGIDs</key>
+                                               <array/>
+                                               <key>XCIncludePerspectivesSwitch</key>
+                                               <integer>1</integer>
+                                               <key>XCSharingToken</key>
+                                               <string>com.apple.Xcode.GFSharingToken</string>
+                                       </dict>
+                                       <key>GeometryConfiguration</key>
+                                       <dict>
+                                               <key>Frame</key>
+                                               <string>{{0, 0}, {203, 355}}</string>
+                                               <key>GroupTreeTableConfiguration</key>
+                                               <array>
+                                                       <string>MainColumn</string>
+                                                       <real>186</real>
+                                               </array>
+                                               <key>RubberWindowFrame</key>
+                                               <string>373 269 690 397 0 0 1440 878 </string>
+                                       </dict>
+                                       <key>Module</key>
+                                       <string>PBXSmartGroupTreeModule</string>
+                                       <key>Proportion</key>
+                                       <string>100%</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Morph</string>
+                       <key>PreferredWidth</key>
+                       <integer>300</integer>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>XCModuleDock</string>
+                               <string>PBXSmartGroupTreeModule</string>
+                       </array>
+                       <key>TableOfContents</key>
+                       <array>
+                               <string>11E0B1FE06471DED0097A5F4</string>
+                       </array>
+                       <key>ToolbarConfiguration</key>
+                       <string>xcode.toolbar.config.default.shortV3</string>
+               </dict>
+       </array>
+       <key>PerspectivesBarVisible</key>
+       <false/>
+       <key>ShelfIsVisible</key>
+       <false/>
+       <key>SourceDescription</key>
+       <string>file at '/Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources/XCPerspectivesSpecificationMode1.xcperspec'</string>
+       <key>StatusbarIsVisible</key>
+       <true/>
+       <key>TimeStamp</key>
+       <real>0.0</real>
+       <key>ToolbarConfigUserDefaultsMinorVersion</key>
+       <string>2</string>
+       <key>ToolbarDisplayMode</key>
+       <integer>1</integer>
+       <key>ToolbarIsVisible</key>
+       <true/>
+       <key>ToolbarSizeMode</key>
+       <integer>1</integer>
+       <key>Type</key>
+       <string>Perspectives</string>
+       <key>UpdateMessage</key>
+       <string>The Default Workspace in this version of Xcode now includes support to hide and show the detail view (what has been referred to as the "Metro-Morph" feature).  You must discard your current Default Workspace settings and update to the latest Default Workspace in order to gain this feature.  Do you wish to update to the latest Workspace defaults for project '%@'?</string>
+       <key>WindowJustification</key>
+       <integer>5</integer>
+       <key>WindowOrderList</key>
+       <array>
+               <string>1CD10A99069EF8BA00B06720</string>
+               <string>98DF31FF144EDB8D001381F0</string>
+               <string>98DF3405144EE6D2001381F0</string>
+               <string>984B104E14AB4E96004BF082</string>
+               <string>98DF3409144EE6D2001381F0</string>
+               <string>984B104914AB4E96004BF082</string>
+               <string>98DF3512144F0095001381F0</string>
+               <string>98DF3515144F0095001381F0</string>
+               <string>98B602FA144F0D5200E746E6</string>
+               <string>984B104214AB4E96004BF082</string>
+               <string>98E0622A145879DF008FCAF3</string>
+               <string>984B103E14AB4E96004BF082</string>
+               <string>1C78EAAD065D492600B07095</string>
+               <string>984B10EC14AB6716004BF082</string>
+               <string>98B7EBAE1491163B000EE462</string>
+               <string>98E0622E145879DF008FCAF3</string>
+               <string>/Users/fnp/wl-mobi/Wolne Lektury.xcodeproj</string>
+       </array>
+       <key>WindowString</key>
+       <string>-1 146 1280 632 0 0 1280 778 </string>
+       <key>WindowToolsV3</key>
+       <array>
+               <dict>
+                       <key>FirstTimeWindowDisplayed</key>
+                       <false/>
+                       <key>Identifier</key>
+                       <string>windowTool.build</string>
+                       <key>IsVertical</key>
+                       <true/>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1CD0528F0623707200166675</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string></string>
+                                                               <key>StatusBarVisibility</key>
+                                                               <true/>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 0}, {500, 218}}</string>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>394 219 500 500 0 0 1280 778 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>PBXNavigatorGroup</string>
+                                                       <key>Proportion</key>
+                                                       <string>218pt</string>
+                                               </dict>
+                                               <dict>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>XCMainBuildResultsModuleGUID</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>Build Results</string>
+                                                               <key>XCBuildResultsTrigger_Collapse</key>
+                                                               <integer>1021</integer>
+                                                               <key>XCBuildResultsTrigger_Open</key>
+                                                               <integer>1011</integer>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 223}, {500, 236}}</string>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>394 219 500 500 0 0 1280 778 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>PBXBuildResultsModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>236pt</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>459pt</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Build Results</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>PBXBuildResultsModule</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <true/>
+                       <key>TableOfContents</key>
+                       <array>
+                               <string>98DF31FF144EDB8D001381F0</string>
+                               <string>984B107C14AB4EFB004BF082</string>
+                               <string>1CD0528F0623707200166675</string>
+                               <string>XCMainBuildResultsModuleGUID</string>
+                       </array>
+                       <key>ToolbarConfiguration</key>
+                       <string>xcode.toolbar.config.buildV3</string>
+                       <key>WindowContentMinSize</key>
+                       <string>486 300</string>
+                       <key>WindowString</key>
+                       <string>394 219 500 500 0 0 1280 778 </string>
+                       <key>WindowToolGUID</key>
+                       <string>98DF31FF144EDB8D001381F0</string>
+                       <key>WindowToolIsVisible</key>
+                       <false/>
+               </dict>
+               <dict>
+                       <key>FirstTimeWindowDisplayed</key>
+                       <false/>
+                       <key>Identifier</key>
+                       <string>windowTool.debugger</string>
+                       <key>IsVertical</key>
+                       <true/>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>Debugger</key>
+                                                               <dict>
+                                                                       <key>HorizontalSplitView</key>
+                                                                       <dict>
+                                                                               <key>_collapsingFrameDimension</key>
+                                                                               <real>0.0</real>
+                                                                               <key>_indexOfCollapsedView</key>
+                                                                               <integer>0</integer>
+                                                                               <key>_percentageOfCollapsedView</key>
+                                                                               <real>0.0</real>
+                                                                               <key>isCollapsed</key>
+                                                                               <string>yes</string>
+                                                                               <key>sizes</key>
+                                                                               <array>
+                                                                                       <string>{{0, 0}, {316, 203}}</string>
+                                                                                       <string>{{316, 0}, {378, 203}}</string>
+                                                                               </array>
+                                                                       </dict>
+                                                                       <key>VerticalSplitView</key>
+                                                                       <dict>
+                                                                               <key>_collapsingFrameDimension</key>
+                                                                               <real>0.0</real>
+                                                                               <key>_indexOfCollapsedView</key>
+                                                                               <integer>0</integer>
+                                                                               <key>_percentageOfCollapsedView</key>
+                                                                               <real>0.0</real>
+                                                                               <key>isCollapsed</key>
+                                                                               <string>yes</string>
+                                                                               <key>sizes</key>
+                                                                               <array>
+                                                                                       <string>{{0, 0}, {694, 203}}</string>
+                                                                                       <string>{{0, 203}, {694, 178}}</string>
+                                                                               </array>
+                                                                       </dict>
+                                                               </dict>
+                                                               <key>LauncherConfigVersion</key>
+                                                               <string>8</string>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1C162984064C10D400B95A72</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>Debug - GLUTExamples (Underwater)</string>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>DebugConsoleVisible</key>
+                                                               <string>None</string>
+                                                               <key>DebugConsoleWindowFrame</key>
+                                                               <string>{{200, 200}, {500, 300}}</string>
+                                                               <key>DebugSTDIOWindowFrame</key>
+                                                               <string>{{200, 200}, {500, 300}}</string>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 0}, {694, 381}}</string>
+                                                               <key>PBXDebugSessionStackFrameViewKey</key>
+                                                               <dict>
+                                                                       <key>DebugVariablesTableConfiguration</key>
+                                                                       <array>
+                                                                               <string>Name</string>
+                                                                               <real>120</real>
+                                                                               <string>Value</string>
+                                                                               <real>85</real>
+                                                                               <string>Summary</string>
+                                                                               <real>148</real>
+                                                                       </array>
+                                                                       <key>Frame</key>
+                                                                       <string>{{316, 0}, {378, 203}}</string>
+                                                                       <key>RubberWindowFrame</key>
+                                                                       <string>267 301 694 422 0 0 1280 778 </string>
+                                                               </dict>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>267 301 694 422 0 0 1280 778 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>PBXDebugSessionModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>381pt</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>381pt</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Debugger</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>PBXDebugSessionModule</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <true/>
+                       <key>TableOfContents</key>
+                       <array>
+                               <string>1CD10A99069EF8BA00B06720</string>
+                               <string>984B107D14AB4EFB004BF082</string>
+                               <string>1C162984064C10D400B95A72</string>
+                               <string>984B107E14AB4EFB004BF082</string>
+                               <string>984B107F14AB4EFB004BF082</string>
+                               <string>984B108014AB4EFB004BF082</string>
+                               <string>984B108114AB4EFB004BF082</string>
+                               <string>984B108214AB4EFB004BF082</string>
+                       </array>
+                       <key>ToolbarConfiguration</key>
+                       <string>xcode.toolbar.config.debugV3</string>
+                       <key>WindowString</key>
+                       <string>267 301 694 422 0 0 1280 778 </string>
+                       <key>WindowToolGUID</key>
+                       <string>1CD10A99069EF8BA00B06720</string>
+                       <key>WindowToolIsVisible</key>
+                       <false/>
+               </dict>
+               <dict>
+                       <key>FirstTimeWindowDisplayed</key>
+                       <false/>
+                       <key>Identifier</key>
+                       <string>windowTool.find</string>
+                       <key>IsVertical</key>
+                       <true/>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>Dock</key>
+                                                       <array>
+                                                               <dict>
+                                                                       <key>ContentConfiguration</key>
+                                                                       <dict>
+                                                                               <key>PBXProjectModuleGUID</key>
+                                                                               <string>1CDD528C0622207200134675</string>
+                                                                               <key>PBXProjectModuleLabel</key>
+                                                                               <string></string>
+                                                                               <key>StatusBarVisibility</key>
+                                                                               <true/>
+                                                                       </dict>
+                                                                       <key>GeometryConfiguration</key>
+                                                                       <dict>
+                                                                               <key>Frame</key>
+                                                                               <string>{{0, 0}, {781, 212}}</string>
+                                                                               <key>RubberWindowFrame</key>
+                                                                               <string>105 217 781 470 0 0 1280 778 </string>
+                                                                       </dict>
+                                                                       <key>Module</key>
+                                                                       <string>PBXNavigatorGroup</string>
+                                                                       <key>Proportion</key>
+                                                                       <string>781pt</string>
+                                                               </dict>
+                                                       </array>
+                                                       <key>Proportion</key>
+                                                       <string>212pt</string>
+                                               </dict>
+                                               <dict>
+                                                       <key>BecomeActive</key>
+                                                       <true/>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1CD0528E0623707200166675</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>Project Find</string>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 217}, {781, 212}}</string>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>105 217 781 470 0 0 1280 778 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>PBXProjectFindModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>212pt</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>429pt</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Project Find</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>PBXProjectFindModule</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <true/>
+                       <key>TableOfContents</key>
+                       <array>
+                               <string>1C530D57069F1CE1000CFCEE</string>
+                               <string>98B602EA144F0C5200E746E6</string>
+                               <string>98B602EB144F0C5200E746E6</string>
+                               <string>1CDD528C0622207200134675</string>
+                               <string>1CD0528E0623707200166675</string>
+                       </array>
+                       <key>WindowString</key>
+                       <string>105 217 781 470 0 0 1280 778 </string>
+                       <key>WindowToolGUID</key>
+                       <string>1C530D57069F1CE1000CFCEE</string>
+                       <key>WindowToolIsVisible</key>
+                       <false/>
+               </dict>
+               <dict>
+                       <key>Identifier</key>
+                       <string>MENUSEPARATOR</string>
+               </dict>
+               <dict>
+                       <key>FirstTimeWindowDisplayed</key>
+                       <false/>
+                       <key>Identifier</key>
+                       <string>windowTool.debuggerConsole</string>
+                       <key>IsVertical</key>
+                       <true/>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>BecomeActive</key>
+                                                       <true/>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1C78EAAC065D492600B07095</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>Debugger Console</string>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 0}, {650, 209}}</string>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>514 169 650 250 0 0 1280 778 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>PBXDebugCLIModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>209pt</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>209pt</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Debugger Console</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>PBXDebugCLIModule</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <true/>
+                       <key>TableOfContents</key>
+                       <array>
+                               <string>1C78EAAD065D492600B07095</string>
+                               <string>984B109214AB4F1D004BF082</string>
+                               <string>1C78EAAC065D492600B07095</string>
+                       </array>
+                       <key>ToolbarConfiguration</key>
+                       <string>xcode.toolbar.config.consoleV3</string>
+                       <key>WindowString</key>
+                       <string>514 169 650 250 0 0 1280 778 </string>
+                       <key>WindowToolGUID</key>
+                       <string>1C78EAAD065D492600B07095</string>
+                       <key>WindowToolIsVisible</key>
+                       <true/>
+               </dict>
+               <dict>
+                       <key>FirstTimeWindowDisplayed</key>
+                       <false/>
+                       <key>Identifier</key>
+                       <string>windowTool.snapshots</string>
+                       <key>IsVertical</key>
+                       <true/>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>98DF3239144EDDA2001381F0</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>Snapshots</string>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 0}, {300, 509}}</string>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>129 194 300 550 0 0 1280 778 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>XCSnapshotModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>509pt</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>509pt</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Snapshots</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>XCSnapshotModule</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <true/>
+                       <key>TableOfContents</key>
+                       <array>
+                               <string>98DF323A144EDDA2001381F0</string>
+                               <string>98DF323B144EDDA2001381F0</string>
+                               <string>98DF3239144EDDA2001381F0</string>
+                       </array>
+                       <key>ToolbarConfiguration</key>
+                       <string>xcode.toolbar.config.snapshots</string>
+                       <key>WindowString</key>
+                       <string>129 194 300 550 0 0 1280 778 </string>
+                       <key>WindowToolGUID</key>
+                       <string>98DF323A144EDDA2001381F0</string>
+                       <key>WindowToolIsVisible</key>
+                       <false/>
+               </dict>
+               <dict>
+                       <key>Identifier</key>
+                       <string>windowTool.scm</string>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1C78EAB2065D492600B07095</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>&lt;No Editor&gt;</string>
+                                                               <key>PBXSplitModuleInNavigatorKey</key>
+                                                               <dict>
+                                                                       <key>Split0</key>
+                                                                       <dict>
+                                                                               <key>PBXProjectModuleGUID</key>
+                                                                               <string>1C78EAB3065D492600B07095</string>
+                                                                       </dict>
+                                                                       <key>SplitCount</key>
+                                                                       <string>1</string>
+                                                               </dict>
+                                                               <key>StatusBarVisibility</key>
+                                                               <integer>1</integer>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 0}, {452, 0}}</string>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>743 379 452 308 0 0 1280 1002 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>PBXNavigatorGroup</string>
+                                                       <key>Proportion</key>
+                                                       <string>0pt</string>
+                                               </dict>
+                                               <dict>
+                                                       <key>BecomeActive</key>
+                                                       <integer>1</integer>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1CD052920623707200166675</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>SCM</string>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>ConsoleFrame</key>
+                                                               <string>{{0, 259}, {452, 0}}</string>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 7}, {452, 259}}</string>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>743 379 452 308 0 0 1280 1002 </string>
+                                                               <key>TableConfiguration</key>
+                                                               <array>
+                                                                       <string>Status</string>
+                                                                       <real>30</real>
+                                                                       <string>FileName</string>
+                                                                       <real>199</real>
+                                                                       <string>Path</string>
+                                                                       <real>197.0950012207031</real>
+                                                               </array>
+                                                               <key>TableFrame</key>
+                                                               <string>{{0, 0}, {452, 250}}</string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>PBXCVSModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>262pt</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>266pt</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>SCM</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>PBXCVSModule</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <integer>1</integer>
+                       <key>TableOfContents</key>
+                       <array>
+                               <string>1C78EAB4065D492600B07095</string>
+                               <string>1C78EAB5065D492600B07095</string>
+                               <string>1C78EAB2065D492600B07095</string>
+                               <string>1CD052920623707200166675</string>
+                       </array>
+                       <key>ToolbarConfiguration</key>
+                       <string>xcode.toolbar.config.scm</string>
+                       <key>WindowString</key>
+                       <string>743 379 452 308 0 0 1280 1002 </string>
+               </dict>
+               <dict>
+                       <key>Identifier</key>
+                       <string>windowTool.breakpoints</string>
+                       <key>IsVertical</key>
+                       <integer>0</integer>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>BecomeActive</key>
+                                                       <integer>1</integer>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>PBXBottomSmartGroupGIDs</key>
+                                                               <array>
+                                                                       <string>1C77FABC04509CD000000102</string>
+                                                               </array>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1CE0B1FE06471DED0097A5F4</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>Files</string>
+                                                               <key>PBXProjectStructureProvided</key>
+                                                               <string>no</string>
+                                                               <key>PBXSmartGroupTreeModuleColumnData</key>
+                                                               <dict>
+                                                                       <key>PBXSmartGroupTreeModuleColumnWidthsKey</key>
+                                                                       <array>
+                                                                               <real>168</real>
+                                                                       </array>
+                                                                       <key>PBXSmartGroupTreeModuleColumnsKey_v4</key>
+                                                                       <array>
+                                                                               <string>MainColumn</string>
+                                                                       </array>
+                                                               </dict>
+                                                               <key>PBXSmartGroupTreeModuleOutlineStateKey_v7</key>
+                                                               <dict>
+                                                                       <key>PBXSmartGroupTreeModuleOutlineStateExpansionKey</key>
+                                                                       <array>
+                                                                               <string>1C77FABC04509CD000000102</string>
+                                                                       </array>
+                                                                       <key>PBXSmartGroupTreeModuleOutlineStateSelectionKey</key>
+                                                                       <array>
+                                                                               <array>
+                                                                                       <integer>0</integer>
+                                                                               </array>
+                                                                       </array>
+                                                                       <key>PBXSmartGroupTreeModuleOutlineStateVisibleRectKey</key>
+                                                                       <string>{{0, 0}, {168, 350}}</string>
+                                                               </dict>
+                                                               <key>PBXTopSmartGroupGIDs</key>
+                                                               <array/>
+                                                               <key>XCIncludePerspectivesSwitch</key>
+                                                               <integer>0</integer>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 0}, {185, 368}}</string>
+                                                               <key>GroupTreeTableConfiguration</key>
+                                                               <array>
+                                                                       <string>MainColumn</string>
+                                                                       <real>168</real>
+                                                               </array>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>315 424 744 409 0 0 1440 878 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>PBXSmartGroupTreeModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>185pt</string>
+                                               </dict>
+                                               <dict>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1CA1AED706398EBD00589147</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>Detail</string>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>Frame</key>
+                                                               <string>{{190, 0}, {554, 368}}</string>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>315 424 744 409 0 0 1440 878 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>XCDetailModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>554pt</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>368pt</string>
+                               </dict>
+                       </array>
+                       <key>MajorVersion</key>
+                       <integer>3</integer>
+                       <key>MinorVersion</key>
+                       <integer>0</integer>
+                       <key>Name</key>
+                       <string>Breakpoints</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>PBXSmartGroupTreeModule</string>
+                               <string>XCDetailModule</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <integer>1</integer>
+                       <key>TableOfContents</key>
+                       <array>
+                               <string>1CDDB66807F98D9800BB5817</string>
+                               <string>1CDDB66907F98D9800BB5817</string>
+                               <string>1CE0B1FE06471DED0097A5F4</string>
+                               <string>1CA1AED706398EBD00589147</string>
+                       </array>
+                       <key>ToolbarConfiguration</key>
+                       <string>xcode.toolbar.config.breakpointsV3</string>
+                       <key>WindowString</key>
+                       <string>315 424 744 409 0 0 1440 878 </string>
+                       <key>WindowToolGUID</key>
+                       <string>1CDDB66807F98D9800BB5817</string>
+                       <key>WindowToolIsVisible</key>
+                       <integer>1</integer>
+               </dict>
+               <dict>
+                       <key>Identifier</key>
+                       <string>windowTool.debugAnimator</string>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>Module</key>
+                                                       <string>PBXNavigatorGroup</string>
+                                                       <key>Proportion</key>
+                                                       <string>100%</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>100%</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Debug Visualizer</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>PBXNavigatorGroup</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <integer>1</integer>
+                       <key>ToolbarConfiguration</key>
+                       <string>xcode.toolbar.config.debugAnimatorV3</string>
+                       <key>WindowString</key>
+                       <string>100 100 700 500 0 0 1280 1002 </string>
+               </dict>
+               <dict>
+                       <key>Identifier</key>
+                       <string>windowTool.bookmarks</string>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>Module</key>
+                                                       <string>PBXBookmarksModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>100%</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>100%</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Bookmarks</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>PBXBookmarksModule</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <integer>0</integer>
+                       <key>WindowString</key>
+                       <string>538 42 401 187 0 0 1280 1002 </string>
+               </dict>
+               <dict>
+                       <key>Identifier</key>
+                       <string>windowTool.projectFormatConflicts</string>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>Module</key>
+                                                       <string>XCProjectFormatConflictsModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>100%</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>100%</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Project Format Conflicts</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>XCProjectFormatConflictsModule</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <integer>0</integer>
+                       <key>WindowContentMinSize</key>
+                       <string>450 300</string>
+                       <key>WindowString</key>
+                       <string>50 850 472 307 0 0 1440 877</string>
+               </dict>
+               <dict>
+                       <key>Identifier</key>
+                       <string>windowTool.classBrowser</string>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>BecomeActive</key>
+                                                       <integer>1</integer>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>OptionsSetName</key>
+                                                               <string>Hierarchy, all classes</string>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1CA6456E063B45B4001379D8</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>Class Browser - NSObject</string>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>ClassesFrame</key>
+                                                               <string>{{0, 0}, {374, 96}}</string>
+                                                               <key>ClassesTreeTableConfiguration</key>
+                                                               <array>
+                                                                       <string>PBXClassNameColumnIdentifier</string>
+                                                                       <real>208</real>
+                                                                       <string>PBXClassBookColumnIdentifier</string>
+                                                                       <real>22</real>
+                                                               </array>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 0}, {630, 331}}</string>
+                                                               <key>MembersFrame</key>
+                                                               <string>{{0, 105}, {374, 395}}</string>
+                                                               <key>MembersTreeTableConfiguration</key>
+                                                               <array>
+                                                                       <string>PBXMemberTypeIconColumnIdentifier</string>
+                                                                       <real>22</real>
+                                                                       <string>PBXMemberNameColumnIdentifier</string>
+                                                                       <real>216</real>
+                                                                       <string>PBXMemberTypeColumnIdentifier</string>
+                                                                       <real>97</real>
+                                                                       <string>PBXMemberBookColumnIdentifier</string>
+                                                                       <real>22</real>
+                                                               </array>
+                                                               <key>PBXModuleWindowStatusBarHidden2</key>
+                                                               <integer>1</integer>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>385 179 630 352 0 0 1440 878 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>PBXClassBrowserModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>332pt</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>332pt</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Class Browser</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>PBXClassBrowserModule</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <integer>0</integer>
+                       <key>TableOfContents</key>
+                       <array>
+                               <string>1C0AD2AF069F1E9B00FABCE6</string>
+                               <string>1C0AD2B0069F1E9B00FABCE6</string>
+                               <string>1CA6456E063B45B4001379D8</string>
+                       </array>
+                       <key>ToolbarConfiguration</key>
+                       <string>xcode.toolbar.config.classbrowser</string>
+                       <key>WindowString</key>
+                       <string>385 179 630 352 0 0 1440 878 </string>
+                       <key>WindowToolGUID</key>
+                       <string>1C0AD2AF069F1E9B00FABCE6</string>
+                       <key>WindowToolIsVisible</key>
+                       <integer>0</integer>
+               </dict>
+               <dict>
+                       <key>Identifier</key>
+                       <string>windowTool.refactoring</string>
+                       <key>IncludeInToolsMenu</key>
+                       <integer>0</integer>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>BecomeActive</key>
+                                                       <integer>1</integer>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>Frame</key>
+                                                               <string>{0, 0}, {500, 335}</string>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>{0, 0}, {500, 335}</string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>XCRefactoringModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>100%</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>100%</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Refactoring</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>XCRefactoringModule</string>
+                       </array>
+                       <key>WindowString</key>
+                       <string>200 200 500 356 0 0 1920 1200 </string>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/Wolne Lektury.xcodeproj/fnp.pbxuser b/Wolne Lektury.xcodeproj/fnp.pbxuser
new file mode 100644 (file)
index 0000000..c528161
--- /dev/null
@@ -0,0 +1,1085 @@
+// !$*UTF8*$!
+{
+       1D3623240D0F684500981E51 /* AppDelegate.h */ = {
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {705, 430}}";
+                       sepNavSelRange = "{0, 0}";
+                       sepNavVisRange = "{0, 646}";
+                       sepNavWindowFrame = "{{15, 215}, {750, 558}}";
+               };
+       };
+       1D3623250D0F684500981E51 /* AppDelegate.m */ = {
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {782, 1807}}";
+                       sepNavSelRange = "{0, 0}";
+                       sepNavVisRange = "{0, 749}";
+                       sepNavWindowFrame = "{{130, 110}, {750, 558}}";
+               };
+       };
+       1D6058900D05DD3D006BFB54 /* Wolne Lektury */ = {
+               activeExec = 0;
+               executables = (
+                       9879B548144ED51F0012352D /* Wolne Lektury */,
+               );
+       };
+       29B97313FDCFA39411CA2CEA /* Project object */ = {
+               activeBuildConfigurationName = Debug;
+               activeExecutable = 9879B548144ED51F0012352D /* Wolne Lektury */;
+               activeTarget = 1D6058900D05DD3D006BFB54 /* Wolne Lektury */;
+               addToTargets = (
+                       1D6058900D05DD3D006BFB54 /* Wolne Lektury */,
+               );
+               codeSenseManager = 9879B567144ED5290012352D /* Code sense */;
+               executables = (
+                       9879B548144ED51F0012352D /* Wolne Lektury */,
+               );
+               perUserDictionary = {
+                       PBXConfiguration.PBXFileTableDataSource3.PBXBookmarksDataSource = {
+                               PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
+                               PBXFileTableDataSourceColumnSortingKey = PBXBookmarksDataSource_NameID;
+                               PBXFileTableDataSourceColumnWidthsKey = (
+                                       200,
+                                       200,
+                                       151,
+                               );
+                               PBXFileTableDataSourceColumnsKey = (
+                                       PBXBookmarksDataSource_LocationID,
+                                       PBXBookmarksDataSource_NameID,
+                                       PBXBookmarksDataSource_CommentsID,
+                               );
+                       };
+                       PBXConfiguration.PBXFileTableDataSource3.PBXExecutablesDataSource = {
+                               PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
+                               PBXFileTableDataSourceColumnSortingKey = PBXExecutablesDataSource_NameID;
+                               PBXFileTableDataSourceColumnWidthsKey = (
+                                       22,
+                                       300,
+                                       721,
+                               );
+                               PBXFileTableDataSourceColumnsKey = (
+                                       PBXExecutablesDataSource_ActiveFlagID,
+                                       PBXExecutablesDataSource_NameID,
+                                       PBXExecutablesDataSource_CommentsID,
+                               );
+                       };
+                       PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = {
+                               PBXFileTableDataSourceColumnSortingDirectionKey = 1;
+                               PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID;
+                               PBXFileTableDataSourceColumnWidthsKey = (
+                                       20,
+                                       833,
+                                       20,
+                                       48,
+                                       43,
+                                       43,
+                                       20,
+                               );
+                               PBXFileTableDataSourceColumnsKey = (
+                                       PBXFileDataSource_FiletypeID,
+                                       PBXFileDataSource_Filename_ColumnID,
+                                       PBXFileDataSource_Built_ColumnID,
+                                       PBXFileDataSource_ObjectSize_ColumnID,
+                                       PBXFileDataSource_Errors_ColumnID,
+                                       PBXFileDataSource_Warnings_ColumnID,
+                                       PBXFileDataSource_Target_ColumnID,
+                               );
+                       };
+                       PBXConfiguration.PBXFileTableDataSource3.PBXFindDataSource = {
+                               PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
+                               PBXFileTableDataSourceColumnSortingKey = PBXFindDataSource_LocationID;
+                               PBXFileTableDataSourceColumnWidthsKey = (
+                                       200,
+                                       847,
+                               );
+                               PBXFileTableDataSourceColumnsKey = (
+                                       PBXFindDataSource_MessageID,
+                                       PBXFindDataSource_LocationID,
+                               );
+                       };
+                       PBXConfiguration.PBXFileTableDataSource3.PBXSymbolsDataSource = {
+                               PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
+                               PBXFileTableDataSourceColumnSortingKey = PBXSymbolsDataSource_SymbolNameID;
+                               PBXFileTableDataSourceColumnWidthsKey = (
+                                       16,
+                                       200,
+                                       50,
+                                       773,
+                               );
+                               PBXFileTableDataSourceColumnsKey = (
+                                       PBXSymbolsDataSource_SymbolTypeIconID,
+                                       PBXSymbolsDataSource_SymbolNameID,
+                                       PBXSymbolsDataSource_SymbolTypeID,
+                                       PBXSymbolsDataSource_ReferenceNameID,
+                               );
+                       };
+                       PBXConfiguration.PBXTargetDataSource.PBXTargetDataSource = {
+                               PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
+                               PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID;
+                               PBXFileTableDataSourceColumnWidthsKey = (
+                                       20,
+                                       793,
+                                       60,
+                                       20,
+                                       48,
+                                       43,
+                                       43,
+                               );
+                               PBXFileTableDataSourceColumnsKey = (
+                                       PBXFileDataSource_FiletypeID,
+                                       PBXFileDataSource_Filename_ColumnID,
+                                       PBXTargetDataSource_PrimaryAttribute,
+                                       PBXFileDataSource_Built_ColumnID,
+                                       PBXFileDataSource_ObjectSize_ColumnID,
+                                       PBXFileDataSource_Errors_ColumnID,
+                                       PBXFileDataSource_Warnings_ColumnID,
+                               );
+                       };
+                       PBXPerProjectTemplateStateSaveDate = 346771179;
+                       PBXWorkspaceStateSaveDate = 346771179;
+               };
+               perUserProjectItems = {
+                       984B102614AB4E96004BF082 /* PBXTextBookmark */ = 984B102614AB4E96004BF082 /* PBXTextBookmark */;
+                       984B102714AB4E96004BF082 /* PBXTextBookmark */ = 984B102714AB4E96004BF082 /* PBXTextBookmark */;
+                       984B102814AB4E96004BF082 /* PBXTextBookmark */ = 984B102814AB4E96004BF082 /* PBXTextBookmark */;
+                       984B102914AB4E96004BF082 /* PlistBookmark */ = 984B102914AB4E96004BF082 /* PlistBookmark */;
+                       984B102A14AB4E96004BF082 /* PBXTextBookmark */ = 984B102A14AB4E96004BF082 /* PBXTextBookmark */;
+                       984B102B14AB4E96004BF082 /* PBXTextBookmark */ = 984B102B14AB4E96004BF082 /* PBXTextBookmark */;
+                       984B102C14AB4E96004BF082 /* PlistBookmark */ = 984B102C14AB4E96004BF082 /* PlistBookmark */;
+                       984B102D14AB4E96004BF082 /* PBXTextBookmark */ = 984B102D14AB4E96004BF082 /* PBXTextBookmark */;
+                       984B102E14AB4E96004BF082 /* PBXBookmark */ = 984B102E14AB4E96004BF082 /* PBXBookmark */;
+                       984B103014AB4E96004BF082 /* PBXTextBookmark */ = 984B103014AB4E96004BF082 /* PBXTextBookmark */;
+                       984B103614AB4E96004BF082 /* PBXTextBookmark */ = 984B103614AB4E96004BF082 /* PBXTextBookmark */;
+                       984B103914AB4E96004BF082 /* PBXTextBookmark */ = 984B103914AB4E96004BF082 /* PBXTextBookmark */;
+                       984B103C14AB4E96004BF082 /* PBXTextBookmark */ = 984B103C14AB4E96004BF082 /* PBXTextBookmark */;
+                       984B104014AB4E96004BF082 /* PBXTextBookmark */ = 984B104014AB4E96004BF082 /* PBXTextBookmark */;
+                       984B104514AB4E96004BF082 /* PlistBookmark */ = 984B104514AB4E96004BF082 /* PlistBookmark */;
+                       984B104614AB4E96004BF082 /* PBXTextBookmark */ = 984B104614AB4E96004BF082 /* PBXTextBookmark */;
+                       984B104714AB4E96004BF082 /* PBXTextBookmark */ = 984B104714AB4E96004BF082 /* PBXTextBookmark */;
+                       984B104814AB4E96004BF082 /* PBXTextBookmark */ = 984B104814AB4E96004BF082 /* PBXTextBookmark */;
+                       984B104B14AB4E96004BF082 /* PBXTextBookmark */ = 984B104B14AB4E96004BF082 /* PBXTextBookmark */;
+                       984B104D14AB4E96004BF082 /* PBXTextBookmark */ = 984B104D14AB4E96004BF082 /* PBXTextBookmark */;
+                       984B105014AB4E96004BF082 /* PBXTextBookmark */ = 984B105014AB4E96004BF082 /* PBXTextBookmark */;
+                       984B105214AB4E96004BF082 /* PBXTextBookmark */ = 984B105214AB4E96004BF082 /* PBXTextBookmark */;
+                       984B106C14AB4EFB004BF082 /* PBXTextBookmark */ = 984B106C14AB4EFB004BF082 /* PBXTextBookmark */;
+                       984B10EE14AB6716004BF082 /* PBXTextBookmark */ = 984B10EE14AB6716004BF082 /* PBXTextBookmark */;
+                       984B12A814AB6F46004BF082 /* PBXTextBookmark */ = 984B12A814AB6F46004BF082 /* PBXTextBookmark */;
+                       984B12AA14AB6F46004BF082 /* PBXTextBookmark */ = 984B12AA14AB6F46004BF082 /* PBXTextBookmark */;
+                       984B12AC14AB6F46004BF082 /* PBXTextBookmark */ = 984B12AC14AB6F46004BF082 /* PBXTextBookmark */;
+                       984B12AE14AB6F46004BF082 /* PBXTextBookmark */ = 984B12AE14AB6F46004BF082 /* PBXTextBookmark */;
+                       984B12B014AB6F46004BF082 /* PBXTextBookmark */ = 984B12B014AB6F46004BF082 /* PBXTextBookmark */;
+                       984B12B214AB6F46004BF082 /* PBXTextBookmark */ = 984B12B214AB6F46004BF082 /* PBXTextBookmark */;
+                       984B12B414AB6F46004BF082 /* PlistBookmark */ = 984B12B414AB6F46004BF082 /* PlistBookmark */;
+                       984B12B514AB6F46004BF082 /* PBXTextBookmark */ = 984B12B514AB6F46004BF082 /* PBXTextBookmark */;
+                       984B12B614AB6F46004BF082 /* PBXTextBookmark */ = 984B12B614AB6F46004BF082 /* PBXTextBookmark */;
+                       984B12B714AB6F46004BF082 /* PBXTextBookmark */ = 984B12B714AB6F46004BF082 /* PBXTextBookmark */;
+                       984B12B814AB6F46004BF082 /* PBXTextBookmark */ = 984B12B814AB6F46004BF082 /* PBXTextBookmark */;
+                       984B12B914AB6F46004BF082 /* PBXTextBookmark */ = 984B12B914AB6F46004BF082 /* PBXTextBookmark */;
+                       984B12BA14AB6F46004BF082 /* PBXTextBookmark */ = 984B12BA14AB6F46004BF082 /* PBXTextBookmark */;
+                       984B12BC14AB6F46004BF082 /* PBXTextBookmark */ = 984B12BC14AB6F46004BF082 /* PBXTextBookmark */;
+                       98B7EA83149103F1000EE462 /* PBXTextBookmark */ = 98B7EA83149103F1000EE462 /* PBXTextBookmark */;
+                       98B7EAFD14910675000EE462 /* PBXTextBookmark */ = 98B7EAFD14910675000EE462 /* PBXTextBookmark */;
+                       98B7EAFE14910675000EE462 /* PBXTextBookmark */ = 98B7EAFE14910675000EE462 /* PBXTextBookmark */;
+                       98B7EB0314910675000EE462 /* PBXBookmark */ = 98B7EB0314910675000EE462 /* PBXBookmark */;
+                       98B7EB0414910675000EE462 /* PBXBookmark */ = 98B7EB0414910675000EE462 /* PBXBookmark */;
+                       98B7EB0514910675000EE462 /* PBXBookmark */ = 98B7EB0514910675000EE462 /* PBXBookmark */;
+                       98B7EB0614910675000EE462 /* PBXBookmark */ = 98B7EB0614910675000EE462 /* PBXBookmark */;
+                       98B7EB0714910675000EE462 /* PBXBookmark */ = 98B7EB0714910675000EE462 /* PBXBookmark */;
+                       98B7EB0814910675000EE462 /* PBXBookmark */ = 98B7EB0814910675000EE462 /* PBXBookmark */;
+                       98B7EB0914910675000EE462 /* PBXBookmark */ = 98B7EB0914910675000EE462 /* PBXBookmark */;
+                       98B7EB8914911229000EE462 /* PBXTextBookmark */ = 98B7EB8914911229000EE462 /* PBXTextBookmark */;
+                       98B7EBA31491163B000EE462 /* PBXTextBookmark */ = 98B7EBA31491163B000EE462 /* PBXTextBookmark */;
+                       98B7EBA41491163B000EE462 /* PBXTextBookmark */ = 98B7EBA41491163B000EE462 /* PBXTextBookmark */;
+                       98B7EBA61491163B000EE462 /* PBXTextBookmark */ = 98B7EBA61491163B000EE462 /* PBXTextBookmark */;
+                       98B7EBA71491163B000EE462 /* PBXTextBookmark */ = 98B7EBA71491163B000EE462 /* PBXTextBookmark */;
+                       98B7EBA81491163B000EE462 /* PBXTextBookmark */ = 98B7EBA81491163B000EE462 /* PBXTextBookmark */;
+                       98B7EBA91491163B000EE462 /* PBXTextBookmark */ = 98B7EBA91491163B000EE462 /* PBXTextBookmark */;
+                       98DF33E2144EE6D2001381F0 /* PBXTextBookmark */ = 98DF33E2144EE6D2001381F0 /* PBXTextBookmark */;
+               };
+               sourceControlManager = 9879B566144ED5290012352D /* Source Control */;
+               userBuildSettings = {
+               };
+       };
+       29B97316FDCFA39411CA2CEA /* main.m */ = {
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {691, 430}}";
+                       sepNavSelRange = "{0, 0}";
+                       sepNavVisRange = "{0, 364}";
+                       sepNavWindowFrame = "{{107, 131}, {750, 558}}";
+               };
+       };
+       3053AC6E109B7857006FCFE7 /* VERSION */ = {
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {1011, 316}}";
+                       sepNavSelRange = "{0, 0}";
+                       sepNavVisRange = "{0, 13}";
+               };
+       };
+       307D28A1123043350040C0FA /* PhoneGapBuildSettings.xcconfig */ = {
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {1011, 316}}";
+                       sepNavSelRange = "{0, 0}";
+                       sepNavVisRange = "{0, 193}";
+                       sepNavWindowFrame = "{{15, 100}, {658, 673}}";
+               };
+       };
+       30E1352610E2C1420031B30D /* PhoneGap.plist */ = {
+               uiCtxt = {
+                       sepNavWindowFrame = "{{61, 172}, {750, 558}}";
+               };
+       };
+       32CA4F630368D1EE00C91783 /* wl_mobi-Prefix.pch */ = {
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {1011, 316}}";
+                       sepNavSelRange = "{0, 0}";
+                       sepNavVisRange = "{0, 183}";
+                       sepNavWindowFrame = "{{15, 100}, {658, 673}}";
+               };
+       };
+       8D1107310486CEB800E47090 /* wl_mobi-Info.plist */ = {
+               uiCtxt = {
+                       sepNavWindowFrame = "{{15, 100}, {658, 673}}";
+               };
+       };
+       98458DA21450E96400C96A53 /* catalogue.js */ = {
+               isa = PBXFileReference;
+               lastKnownFileType = sourcecode.javascript;
+               name = catalogue.js;
+               path = "/Users/fnp/wl-mobi/www/js/catalogue.js";
+               sourceTree = "<absolute>";
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {768, 5304}}";
+                       sepNavSelRange = "{1358, 0}";
+                       sepNavVisRange = "{0, 955}";
+                       sepNavWindowFrame = "{{273, 168}, {750, 558}}";
+               };
+       };
+       984B102614AB4E96004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 98E06229145879DF008FCAF3 /* filerepo.js */;
+               name = "filerepo.js: 50";
+               rLen = 0;
+               rLoc = 1423;
+               rType = 0;
+               vrLen = 538;
+               vrLoc = 1024;
+       };
+       984B102714AB4E96004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 32CA4F630368D1EE00C91783 /* wl_mobi-Prefix.pch */;
+               name = "wl_mobi-Prefix.pch: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 183;
+               vrLoc = 0;
+       };
+       984B102814AB4E96004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 29B97316FDCFA39411CA2CEA /* main.m */;
+               name = "main.m: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 364;
+               vrLoc = 0;
+       };
+       984B102914AB4E96004BF082 /* PlistBookmark */ = {
+               isa = PlistBookmark;
+               fRef = 8D1107310486CEB800E47090 /* wl_mobi-Info.plist */;
+               fallbackIsa = PBXBookmark;
+               isK = 0;
+               kPath = (
+                       CFBundleIconFiles,
+               );
+               name = "/Users/fnp/wl-mobi/wl_mobi-Info.plist";
+               rLen = 0;
+               rLoc = 9223372036854775808;
+       };
+       984B102A14AB4E96004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 3053AC6E109B7857006FCFE7 /* VERSION */;
+               name = "VERSION: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 13;
+               vrLoc = 0;
+       };
+       984B102B14AB4E96004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 307D28A1123043350040C0FA /* PhoneGapBuildSettings.xcconfig */;
+               name = "PhoneGapBuildSettings.xcconfig: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 193;
+               vrLoc = 0;
+       };
+       984B102C14AB4E96004BF082 /* PlistBookmark */ = {
+               isa = PlistBookmark;
+               fRef = 30E1352610E2C1420031B30D /* PhoneGap.plist */;
+               fallbackIsa = PBXBookmark;
+               isK = 0;
+               kPath = (
+                       EnableViewportScale,
+               );
+               name = "/Users/fnp/wl-mobi/PhoneGap.plist";
+               rLen = 0;
+               rLoc = 9223372036854775808;
+       };
+       984B102D14AB4E96004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 98B7EBB41491163B000EE462 /* book_text.css */;
+               name = "book_text.css: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 496;
+               vrLoc = 0;
+       };
+       984B102E14AB4E96004BF082 /* PBXBookmark */ = {
+               isa = PBXBookmark;
+               fRef = 984B102F14AB4E96004BF082 /* icon-BookText.png */;
+       };
+       984B102F14AB4E96004BF082 /* icon-BookText.png */ = {
+               isa = PBXFileReference;
+               lastKnownFileType = image.png;
+               name = "icon-BookText.png";
+               path = "/Users/fnp/wl-mobi/www/img/icon-BookText.png";
+               sourceTree = "<absolute>";
+       };
+       984B103014AB4E96004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 98E06231145879DF008FCAF3 /* style.css */;
+               name = "style.css: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 462;
+               vrLoc = 0;
+       };
+       984B103214AB4E96004BF082 /* WolneLektury.html */ = {
+               isa = PBXFileReference;
+               lastKnownFileType = text.html;
+               name = WolneLektury.html;
+               path = "/Users/fnp/wl-mobi/www/WolneLektury.html";
+               sourceTree = "<absolute>";
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {1011, 533}}";
+                       sepNavSelRange = "{0, 0}";
+                       sepNavVisRange = "{16, 1330}";
+               };
+       };
+       984B103614AB4E96004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 984B103714AB4E96004BF082 /* book_text.css */;
+               name = "book_text.css: 10";
+               rLen = 0;
+               rLoc = 255;
+               rType = 0;
+               vrLen = 908;
+               vrLoc = 0;
+       };
+       984B103714AB4E96004BF082 /* book_text.css */ = {
+               isa = PBXFileReference;
+               lastKnownFileType = text.css;
+               name = book_text.css;
+               path = "/Users/fnp/wl-mobi/www/css/book_text.css";
+               sourceTree = "<absolute>";
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {614, 2938}}";
+                       sepNavSelRange = "{255, 0}";
+                       sepNavVisRange = "{0, 886}";
+               };
+       };
+       984B103914AB4E96004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 984B103A14AB4E96004BF082 /* style.css */;
+               name = "style.css: 163";
+               rLen = 0;
+               rLoc = 2369;
+               rType = 0;
+               vrLen = 819;
+               vrLoc = 0;
+       };
+       984B103A14AB4E96004BF082 /* style.css */ = {
+               isa = PBXFileReference;
+               lastKnownFileType = text.css;
+               name = style.css;
+               path = "/Users/fnp/wl-mobi/www/css/style.css";
+               sourceTree = "<absolute>";
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {614, 2795}}";
+                       sepNavSelRange = "{1028, 0}";
+                       sepNavVisRange = "{646, 599}";
+               };
+       };
+       984B103C14AB4E96004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 984B103D14AB4E96004BF082 /* view.js */;
+               name = "view.js: 20";
+               rLen = 0;
+               rLoc = 459;
+               rType = 0;
+               vrLen = 908;
+               vrLoc = 199;
+       };
+       984B103D14AB4E96004BF082 /* view.js */ = {
+               isa = PBXFileReference;
+               lastKnownFileType = sourcecode.javascript;
+               name = view.js;
+               path = "/Users/fnp/wl-mobi/www/js/view.js";
+               sourceTree = "<absolute>";
+       };
+       984B104014AB4E96004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 984B104114AB4E96004BF082 /* main.js */;
+               name = "main.js: 63";
+               rLen = 0;
+               rLoc = 1457;
+               rType = 0;
+               vrLen = 905;
+               vrLoc = 666;
+       };
+       984B104114AB4E96004BF082 /* main.js */ = {
+               isa = PBXFileReference;
+               lastKnownFileType = sourcecode.javascript;
+               name = main.js;
+               path = "/Users/fnp/wl-mobi/www/js/main.js";
+               sourceTree = "<absolute>";
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {614, 936}}";
+                       sepNavSelRange = "{760, 0}";
+                       sepNavVisRange = "{309, 903}";
+               };
+       };
+       984B104514AB4E96004BF082 /* PlistBookmark */ = {
+               isa = PlistBookmark;
+               fRef = 30E1352610E2C1420031B30D /* PhoneGap.plist */;
+               fallbackIsa = PBXBookmark;
+               isK = 0;
+               kPath = (
+                       EnableViewportScale,
+               );
+               name = "/Users/fnp/wl-mobi/PhoneGap.plist";
+               rLen = 0;
+               rLoc = 9223372036854775808;
+       };
+       984B104614AB4E96004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 1D3623240D0F684500981E51 /* AppDelegate.h */;
+               name = "AppDelegate.h: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 646;
+               vrLoc = 0;
+       };
+       984B104714AB4E96004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 29B97316FDCFA39411CA2CEA /* main.m */;
+               name = "main.m: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 364;
+               vrLoc = 0;
+       };
+       984B104814AB4E96004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 1D3623250D0F684500981E51 /* AppDelegate.m */;
+               name = "AppDelegate.m: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 749;
+               vrLoc = 0;
+       };
+       984B104B14AB4E96004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 98E06229145879DF008FCAF3 /* filerepo.js */;
+               name = "filerepo.js: 76";
+               rLen = 0;
+               rLoc = 2162;
+               rType = 0;
+               vrLen = 1275;
+               vrLoc = 0;
+       };
+       984B104D14AB4E96004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 98458DA21450E96400C96A53 /* catalogue.js */;
+               name = "catalogue.js: 57";
+               rLen = 0;
+               rLoc = 1358;
+               rType = 0;
+               vrLen = 955;
+               vrLoc = 0;
+       };
+       984B105014AB4E96004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 984B105114AB4E96004BF082 /* history.js */;
+               name = "history.js: 32";
+               rLen = 0;
+               rLoc = 839;
+               rType = 0;
+               vrLen = 956;
+               vrLoc = 90;
+       };
+       984B105114AB4E96004BF082 /* history.js */ = {
+               isa = PBXFileReference;
+               lastKnownFileType = sourcecode.javascript;
+               name = history.js;
+               path = "/Users/fnp/wl-mobi/www/js/history.js";
+               sourceTree = "<absolute>";
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {614, 1352}}";
+                       sepNavSelRange = "{839, 0}";
+                       sepNavVisRange = "{90, 948}";
+               };
+       };
+       984B105214AB4E96004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 984B105314AB4E96004BF082 /* dbput.js */;
+               name = "dbput.js: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 822;
+               vrLoc = 0;
+       };
+       984B105314AB4E96004BF082 /* dbput.js */ = {
+               isa = PBXFileReference;
+               lastKnownFileType = sourcecode.javascript;
+               name = dbput.js;
+               path = "/Users/fnp/wl-mobi/www/js/dbput.js";
+               sourceTree = "<absolute>";
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {691, 494}}";
+                       sepNavSelRange = "{0, 0}";
+                       sepNavVisRange = "{0, 822}";
+                       sepNavWindowFrame = "{{61, 173}, {750, 558}}";
+               };
+       };
+       984B106C14AB4EFB004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 984B103214AB4E96004BF082 /* WolneLektury.html */;
+               name = "WolneLektury.html: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 1410;
+               vrLoc = 16;
+       };
+       984B10EE14AB6716004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 98B7EBA51491163B000EE462 /* menu.js */;
+               name = "menu.js: 25";
+               rLen = 0;
+               rLoc = 1209;
+               rType = 0;
+               vrLen = 1516;
+               vrLoc = 248;
+       };
+       984B12A814AB6F46004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 984B12A914AB6F46004BF082 /* WolneLektury.html */;
+               name = "WolneLektury.html: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 1410;
+               vrLoc = 16;
+       };
+       984B12A914AB6F46004BF082 /* WolneLektury.html */ = {
+               isa = PBXFileReference;
+               name = WolneLektury.html;
+               path = "/Users/fnp/wl-mobi/www/WolneLektury.html";
+               sourceTree = "<absolute>";
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {1011, 533}}";
+                       sepNavSelRange = "{0, 0}";
+                       sepNavVisRange = "{16, 1410}";
+               };
+       };
+       984B12AA14AB6F46004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 984B12AB14AB6F46004BF082 /* style.css */;
+               name = "style.css: 59";
+               rLen = 0;
+               rLoc = 1028;
+               rType = 0;
+               vrLen = 637;
+               vrLoc = 646;
+       };
+       984B12AB14AB6F46004BF082 /* style.css */ = {
+               isa = PBXFileReference;
+               name = style.css;
+               path = "/Users/fnp/wl-mobi/www/css/style.css";
+               sourceTree = "<absolute>";
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {614, 2795}}";
+                       sepNavSelRange = "{1028, 0}";
+                       sepNavVisRange = "{646, 637}";
+                       sepNavWindowFrame = "{{622, 105}, {658, 673}}";
+               };
+       };
+       984B12AC14AB6F46004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 984B12AD14AB6F46004BF082 /* view.js */;
+               name = "view.js: 428";
+               rLen = 10;
+               rLoc = 11532;
+               rType = 0;
+               vrLen = 905;
+               vrLoc = 984;
+       };
+       984B12AD14AB6F46004BF082 /* view.js */ = {
+               isa = PBXFileReference;
+               name = view.js;
+               path = "/Users/fnp/wl-mobi/www/js/view.js";
+               sourceTree = "<absolute>";
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {964, 6474}}";
+                       sepNavSelRange = "{11532, 10}";
+                       sepNavVisRange = "{984, 905}";
+                       sepNavWindowFrame = "{{223, 62}, {658, 673}}";
+               };
+       };
+       984B12AE14AB6F46004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 984B12AF14AB6F46004BF082 /* menu.js */;
+               name = "menu.js: 68";
+               rLen = 0;
+               rLoc = 2417;
+               rType = 0;
+               vrLen = 1574;
+               vrLoc = 0;
+       };
+       984B12AF14AB6F46004BF082 /* menu.js */ = {
+               isa = PBXFileReference;
+               name = menu.js;
+               path = "/Users/fnp/wl-mobi/www/js/menu.js";
+               sourceTree = "<absolute>";
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {999, 923}}";
+                       sepNavSelRange = "{2417, 0}";
+                       sepNavVisRange = "{0, 1574}";
+                       sepNavWindowFrame = "{{607, 105}, {658, 673}}";
+               };
+       };
+       984B12B014AB6F46004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 984B12B114AB6F46004BF082 /* main.js */;
+               name = "main.js: 32";
+               rLen = 0;
+               rLoc = 760;
+               rType = 0;
+               vrLen = 937;
+               vrLoc = 309;
+       };
+       984B12B114AB6F46004BF082 /* main.js */ = {
+               isa = PBXFileReference;
+               name = main.js;
+               path = "/Users/fnp/wl-mobi/www/js/main.js";
+               sourceTree = "<absolute>";
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {614, 936}}";
+                       sepNavSelRange = "{760, 0}";
+                       sepNavVisRange = "{309, 937}";
+                       sepNavWindowFrame = "{{354, 92}, {658, 673}}";
+               };
+       };
+       984B12B214AB6F46004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 984B12B314AB6F46004BF082 /* book_text.css */;
+               name = "book_text.css: 10";
+               rLen = 0;
+               rLoc = 255;
+               rType = 0;
+               vrLen = 908;
+               vrLoc = 0;
+       };
+       984B12B314AB6F46004BF082 /* book_text.css */ = {
+               isa = PBXFileReference;
+               name = book_text.css;
+               path = "/Users/fnp/wl-mobi/www/css/book_text.css";
+               sourceTree = "<absolute>";
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {614, 2938}}";
+                       sepNavSelRange = "{255, 0}";
+                       sepNavVisRange = "{0, 908}";
+                       sepNavWindowFrame = "{{575, 100}, {658, 673}}";
+               };
+       };
+       984B12B414AB6F46004BF082 /* PlistBookmark */ = {
+               isa = PlistBookmark;
+               fRef = 30E1352610E2C1420031B30D /* PhoneGap.plist */;
+               fallbackIsa = PBXBookmark;
+               isK = 0;
+               kPath = (
+                       EnableViewportScale,
+               );
+               name = "/Users/fnp/wl-mobi/PhoneGap.plist";
+               rLen = 0;
+               rLoc = 9223372036854775807;
+       };
+       984B12B514AB6F46004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 1D3623240D0F684500981E51 /* AppDelegate.h */;
+               name = "AppDelegate.h: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 646;
+               vrLoc = 0;
+       };
+       984B12B614AB6F46004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 29B97316FDCFA39411CA2CEA /* main.m */;
+               name = "main.m: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 364;
+               vrLoc = 0;
+       };
+       984B12B714AB6F46004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 1D3623250D0F684500981E51 /* AppDelegate.m */;
+               name = "AppDelegate.m: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 749;
+               vrLoc = 0;
+       };
+       984B12B814AB6F46004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 98E06229145879DF008FCAF3 /* filerepo.js */;
+               name = "filerepo.js: 76";
+               rLen = 0;
+               rLoc = 2162;
+               rType = 0;
+               vrLen = 1275;
+               vrLoc = 0;
+       };
+       984B12B914AB6F46004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 98458DA21450E96400C96A53 /* catalogue.js */;
+               name = "catalogue.js: 57";
+               rLen = 0;
+               rLoc = 1358;
+               rType = 0;
+               vrLen = 955;
+               vrLoc = 0;
+       };
+       984B12BA14AB6F46004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 984B12BB14AB6F46004BF082 /* history.js */;
+               name = "history.js: 32";
+               rLen = 0;
+               rLoc = 839;
+               rType = 0;
+               vrLen = 956;
+               vrLoc = 90;
+       };
+       984B12BB14AB6F46004BF082 /* history.js */ = {
+               isa = PBXFileReference;
+               name = history.js;
+               path = "/Users/fnp/wl-mobi/www/js/history.js";
+               sourceTree = "<absolute>";
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {614, 1352}}";
+                       sepNavSelRange = "{839, 0}";
+                       sepNavVisRange = "{90, 956}";
+                       sepNavWindowFrame = "{{471, 91}, {658, 673}}";
+               };
+       };
+       984B12BC14AB6F46004BF082 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 984B12BD14AB6F46004BF082 /* dbput.js */;
+               name = "dbput.js: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 822;
+               vrLoc = 0;
+       };
+       984B12BD14AB6F46004BF082 /* dbput.js */ = {
+               isa = PBXFileReference;
+               name = dbput.js;
+               path = "/Users/fnp/wl-mobi/www/js/dbput.js";
+               sourceTree = "<absolute>";
+       };
+       9879B548144ED51F0012352D /* Wolne Lektury */ = {
+               isa = PBXExecutable;
+               activeArgIndices = (
+               );
+               argumentStrings = (
+               );
+               autoAttachOnCrash = 1;
+               breakpointsEnabled = 0;
+               configStateDict = {
+               };
+               customDataFormattersEnabled = 1;
+               dataTipCustomDataFormattersEnabled = 1;
+               dataTipShowTypeColumn = 1;
+               dataTipSortType = 0;
+               debuggerPlugin = GDBDebugging;
+               disassemblyDisplayState = 0;
+               dylibVariantSuffix = "";
+               enableDebugStr = 1;
+               environmentEntries = (
+               );
+               executableSystemSymbolLevel = 0;
+               executableUserSymbolLevel = 0;
+               libgmallocEnabled = 0;
+               name = "Wolne Lektury";
+               savedGlobals = {
+               };
+               showTypeColumn = 0;
+               sourceDirectories = (
+               );
+       };
+       9879B566144ED5290012352D /* Source Control */ = {
+               isa = PBXSourceControlManager;
+               fallbackIsa = XCSourceControlManager;
+               isSCMEnabled = 0;
+               scmConfiguration = {
+                       repositoryNamesForRoots = {
+                               "" = "";
+                       };
+               };
+       };
+       9879B567144ED5290012352D /* Code sense */ = {
+               isa = PBXCodeSenseManager;
+               indexTemplatePath = "";
+       };
+       98B602E3144F0C4200E746E6 /* NativeControls.h */ = {
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {1011, 897}}";
+                       sepNavSelRange = "{0, 0}";
+                       sepNavVisRange = "{0, 639}";
+               };
+       };
+       98B602E4144F0C4200E746E6 /* NativeControls.js */ = {
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {1011, 4199}}";
+                       sepNavSelRange = "{0, 0}";
+                       sepNavVisRange = "{17, 640}";
+                       sepNavWindowFrame = "{{38, 194}, {750, 558}}";
+               };
+       };
+       98B602E5144F0C4200E746E6 /* NativeControls.m */ = {
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {1011, 9269}}";
+                       sepNavSelRange = "{0, 0}";
+                       sepNavVisRange = "{0, 579}";
+               };
+       };
+       98B7EA83149103F1000EE462 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 98B7EA84149103F1000EE462 /* Wolne Lektury */;
+               name = "Wolne Lektury: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 0;
+               vrLoc = 0;
+       };
+       98B7EA84149103F1000EE462 /* Wolne Lektury */ = {
+               isa = PBXFileReference;
+               lastKnownFileType = text;
+               name = "Wolne Lektury";
+               path = "/Users/fnp/wl-mobi/Wolne Lektury";
+               sourceTree = "<absolute>";
+       };
+       98B7EAFD14910675000EE462 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 98B602E5144F0C4200E746E6 /* NativeControls.m */;
+               name = "NativeControls.m: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 579;
+               vrLoc = 0;
+       };
+       98B7EAFE14910675000EE462 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 98B602E4144F0C4200E746E6 /* NativeControls.js */;
+               name = "NativeControls.js: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 640;
+               vrLoc = 17;
+       };
+       98B7EB0314910675000EE462 /* PBXBookmark */ = {
+               isa = PBXBookmark;
+               fRef = 308D05301370CCF300D202BF /* icon@2x.png */;
+       };
+       98B7EB0414910675000EE462 /* PBXBookmark */ = {
+               isa = PBXBookmark;
+               fRef = 308D052F1370CCF300D202BF /* icon.png */;
+       };
+       98B7EB0514910675000EE462 /* PBXBookmark */ = {
+               isa = PBXBookmark;
+               fRef = 308D052E1370CCF300D202BF /* icon-72.png */;
+       };
+       98B7EB0614910675000EE462 /* PBXBookmark */ = {
+               isa = PBXBookmark;
+               fRef = 308D05351370CCF300D202BF /* Default@2x.png */;
+       };
+       98B7EB0714910675000EE462 /* PBXBookmark */ = {
+               isa = PBXBookmark;
+               fRef = 308D05341370CCF300D202BF /* Default.png */;
+       };
+       98B7EB0814910675000EE462 /* PBXBookmark */ = {
+               isa = PBXBookmark;
+               fRef = 308D05331370CCF300D202BF /* Default-Portrait.png */;
+       };
+       98B7EB0914910675000EE462 /* PBXBookmark */ = {
+               isa = PBXBookmark;
+               fRef = 308D05321370CCF300D202BF /* Default-Landscape.png */;
+       };
+       98B7EB8914911229000EE462 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 98E06227145879DF008FCAF3 /* main.js */;
+               name = "main.js: 24";
+               rLen = 0;
+               rLoc = 537;
+               rType = 0;
+               vrLen = 544;
+               vrLoc = 210;
+       };
+       98B7EBA31491163B000EE462 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 98458DA21450E96400C96A53 /* catalogue.js */;
+               name = "catalogue.js: 162";
+               rLen = 0;
+               rLoc = 4437;
+               rType = 0;
+               vrLen = 508;
+               vrLoc = 4158;
+       };
+       98B7EBA41491163B000EE462 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 98B7EBA51491163B000EE462 /* menu.js */;
+               name = "menu.js: 40";
+               rLen = 0;
+               rLoc = 1655;
+               rType = 0;
+               vrLen = 726;
+               vrLoc = 1073;
+       };
+       98B7EBA51491163B000EE462 /* menu.js */ = {
+               isa = PBXFileReference;
+               lastKnownFileType = sourcecode.javascript;
+               name = menu.js;
+               path = "/Users/fnp/wl-mobi/www/js/menu.js";
+               sourceTree = "<absolute>";
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {999, 923}}";
+                       sepNavSelRange = "{2417, 0}";
+                       sepNavVisRange = "{0, 1535}";
+               };
+       };
+       98B7EBA61491163B000EE462 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 98DF33F2144EE6D2001381F0 /* view.js */;
+               name = "view.js: 461";
+               rLen = 0;
+               rLoc = 12327;
+               rType = 0;
+               vrLen = 598;
+               vrLoc = 12144;
+       };
+       98B7EBA71491163B000EE462 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 98B602E3144F0C4200E746E6 /* NativeControls.h */;
+               name = "NativeControls.h: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 639;
+               vrLoc = 0;
+       };
+       98B7EBA81491163B000EE462 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 1D3623250D0F684500981E51 /* AppDelegate.m */;
+               name = "AppDelegate.m: 90";
+               rLen = 0;
+               rLoc = 2732;
+               rType = 0;
+               vrLen = 468;
+               vrLoc = 0;
+       };
+       98B7EBA91491163B000EE462 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 1D3623240D0F684500981E51 /* AppDelegate.h */;
+               name = "AppDelegate.h: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 578;
+               vrLoc = 68;
+       };
+       98B7EBB41491163B000EE462 /* book_text.css */ = {
+               isa = PBXFileReference;
+               lastKnownFileType = text.css;
+               name = book_text.css;
+               path = "/Users/fnp/wl-mobi/www/css/book_text.css";
+               sourceTree = "<absolute>";
+       };
+       98DF33E2144EE6D2001381F0 /* PBXTextBookmark */ = {
+               isa = PBXTextBookmark;
+               fRef = 98DF33E3144EE6D2001381F0 /* menuinterface.js */;
+               name = "menuinterface.js: 1";
+               rLen = 0;
+               rLoc = 0;
+               rType = 0;
+               vrLen = 733;
+               vrLoc = 0;
+       };
+       98DF33E3144EE6D2001381F0 /* menuinterface.js */ = {
+               isa = PBXFileReference;
+               lastKnownFileType = sourcecode.javascript;
+               name = menuinterface.js;
+               path = "/Users/fnp/wl-mobi/www/js/menuinterface.js";
+               sourceTree = "<absolute>";
+       };
+       98DF33F2144EE6D2001381F0 /* view.js */ = {
+               isa = PBXFileReference;
+               lastKnownFileType = sourcecode.javascript;
+               name = view.js;
+               path = "/Users/fnp/wl-mobi/www/js/view.js";
+               sourceTree = "<absolute>";
+       };
+       98E06227145879DF008FCAF3 /* main.js */ = {
+               isa = PBXFileReference;
+               lastKnownFileType = sourcecode.javascript;
+               name = main.js;
+               path = "/Users/fnp/wl-mobi/www/js/main.js";
+               sourceTree = "<absolute>";
+       };
+       98E06229145879DF008FCAF3 /* filerepo.js */ = {
+               isa = PBXFileReference;
+               lastKnownFileType = sourcecode.javascript;
+               name = filerepo.js;
+               path = "/Users/fnp/wl-mobi/www/js/filerepo.js";
+               sourceTree = "<absolute>";
+               uiCtxt = {
+                       sepNavIntBoundsRect = "{{0, 0}, {614, 1703}}";
+                       sepNavSelRange = "{2162, 0}";
+                       sepNavVisRange = "{0, 1275}";
+                       sepNavWindowFrame = "{{394, 94}, {658, 673}}";
+               };
+       };
+       98E06231145879DF008FCAF3 /* style.css */ = {
+               isa = PBXFileReference;
+               lastKnownFileType = text.css;
+               name = style.css;
+               path = "/Users/fnp/wl-mobi/www/css/style.css";
+               sourceTree = "<absolute>";
+       };
+}
diff --git a/Wolne Lektury.xcodeproj/project.pbxproj b/Wolne Lektury.xcodeproj/project.pbxproj
new file mode 100755 (executable)
index 0000000..48e6138
--- /dev/null
@@ -0,0 +1,539 @@
+// !$*UTF8*$!
+{
+       archiveVersion = 1;
+       classes = {
+       };
+       objectVersion = 45;
+       objects = {
+
+/* Begin PBXBuildFile section */
+               1D3623260D0F684500981E51 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* AppDelegate.m */; };
+               1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; };
+               1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; };
+               1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
+               288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765FC0DF74451002DB57D /* CoreGraphics.framework */; };
+               301BF552109A68D80062928A /* libPhoneGap.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF535109A57CC0062928A /* libPhoneGap.a */; };
+               301BF5B5109A6A2B0062928A /* AddressBook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5B4109A6A2B0062928A /* AddressBook.framework */; };
+               301BF5B7109A6A2B0062928A /* AddressBookUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5B6109A6A2B0062928A /* AddressBookUI.framework */; };
+               301BF5B9109A6A2B0062928A /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5B8109A6A2B0062928A /* AudioToolbox.framework */; };
+               301BF5BB109A6A2B0062928A /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5BA109A6A2B0062928A /* AVFoundation.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
+               301BF5BD109A6A2B0062928A /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5BC109A6A2B0062928A /* CFNetwork.framework */; };
+               301BF5BF109A6A2B0062928A /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5BE109A6A2B0062928A /* CoreLocation.framework */; };
+               301BF5C1109A6A2B0062928A /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5C0109A6A2B0062928A /* MediaPlayer.framework */; };
+               301BF5C3109A6A2B0062928A /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5C2109A6A2B0062928A /* QuartzCore.framework */; };
+               301BF5C5109A6A2B0062928A /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5C4109A6A2B0062928A /* SystemConfiguration.framework */; };
+               3053AC6F109B7857006FCFE7 /* VERSION in Resources */ = {isa = PBXBuildFile; fileRef = 3053AC6E109B7857006FCFE7 /* VERSION */; };
+               305D5FD1115AB8F900A74A75 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 305D5FD0115AB8F900A74A75 /* MobileCoreServices.framework */; };
+               307D28A2123043360040C0FA /* PhoneGapBuildSettings.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 307D28A1123043350040C0FA /* PhoneGapBuildSettings.xcconfig */; };
+               308D05371370CCF300D202BF /* icon-72.png in Resources */ = {isa = PBXBuildFile; fileRef = 308D052E1370CCF300D202BF /* icon-72.png */; };
+               308D05381370CCF300D202BF /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 308D052F1370CCF300D202BF /* icon.png */; };
+               308D05391370CCF300D202BF /* icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 308D05301370CCF300D202BF /* icon@2x.png */; };
+               308D053A1370CCF300D202BF /* Default-Landscape.png in Resources */ = {isa = PBXBuildFile; fileRef = 308D05321370CCF300D202BF /* Default-Landscape.png */; };
+               308D053B1370CCF300D202BF /* Default-Portrait.png in Resources */ = {isa = PBXBuildFile; fileRef = 308D05331370CCF300D202BF /* Default-Portrait.png */; };
+               308D053C1370CCF300D202BF /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 308D05341370CCF300D202BF /* Default.png */; };
+               308D053D1370CCF300D202BF /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 308D05351370CCF300D202BF /* Default@2x.png */; };
+               30E1352710E2C1420031B30D /* PhoneGap.plist in Resources */ = {isa = PBXBuildFile; fileRef = 30E1352610E2C1420031B30D /* PhoneGap.plist */; };
+               30E5649213A7FCAF007403D8 /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 30E5649113A7FCAF007403D8 /* CoreMedia.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
+               98B602E6144F0C4200E746E6 /* NativeControls.js in Sources */ = {isa = PBXBuildFile; fileRef = 98B602E4144F0C4200E746E6 /* NativeControls.js */; };
+               98B602E7144F0C4200E746E6 /* NativeControls.m in Sources */ = {isa = PBXBuildFile; fileRef = 98B602E5144F0C4200E746E6 /* NativeControls.m */; };
+               98DF3370144EE40D001381F0 /* www in Resources */ = {isa = PBXBuildFile; fileRef = 98DF335B144EE40D001381F0 /* www */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+               301BF534109A57CC0062928A /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 301BF52D109A57CC0062928A /* PhoneGapLib.xcodeproj */;
+                       proxyType = 2;
+                       remoteGlobalIDString = D2AAC07E0554694100DB518D;
+                       remoteInfo = PhoneGapLib;
+               };
+               301BF550109A68C00062928A /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 301BF52D109A57CC0062928A /* PhoneGapLib.xcodeproj */;
+                       proxyType = 1;
+                       remoteGlobalIDString = D2AAC07D0554694100DB518D;
+                       remoteInfo = PhoneGapLib;
+               };
+               30E47BC2136F595F00DBB853 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 301BF52D109A57CC0062928A /* PhoneGapLib.xcodeproj */;
+                       proxyType = 2;
+                       remoteGlobalIDString = 303258D8136B2C9400982B63;
+                       remoteInfo = PhoneGap;
+               };
+               9879B550144ED51F0012352D /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 301BF52D109A57CC0062928A /* PhoneGapLib.xcodeproj */;
+                       proxyType = 2;
+                       remoteGlobalIDString = 686357A9141002F100DF4CF2;
+                       remoteInfo = PhoneGapLibTests;
+               };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+               98DF3300144EE25A001381F0 /* CopyFiles */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 2147483647;
+                       dstPath = www;
+                       dstSubfolderSpec = 7;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               98DF3307144EE28A001381F0 /* CopyFiles */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 2147483647;
+                       dstPath = www;
+                       dstSubfolderSpec = 7;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+               1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+               1D3623240D0F684500981E51 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+               1D3623250D0F684500981E51 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+               1D6058910D05DD3D006BFB54 /* Wolne Lektury.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Wolne Lektury.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+               1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+               288765FC0DF74451002DB57D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
+               29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+               301BF52D109A57CC0062928A /* PhoneGapLib.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = PhoneGapLib.xcodeproj; sourceTree = PHONEGAPLIB; };
+               301BF5B4109A6A2B0062928A /* AddressBook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBook.framework; path = System/Library/Frameworks/AddressBook.framework; sourceTree = SDKROOT; };
+               301BF5B6109A6A2B0062928A /* AddressBookUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBookUI.framework; path = System/Library/Frameworks/AddressBookUI.framework; sourceTree = SDKROOT; };
+               301BF5B8109A6A2B0062928A /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
+               301BF5BA109A6A2B0062928A /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
+               301BF5BC109A6A2B0062928A /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
+               301BF5BE109A6A2B0062928A /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; };
+               301BF5C0109A6A2B0062928A /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; };
+               301BF5C2109A6A2B0062928A /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
+               301BF5C4109A6A2B0062928A /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
+               3053AC6E109B7857006FCFE7 /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = PHONEGAPLIB; };
+               305D5FD0115AB8F900A74A75 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
+               307D28A1123043350040C0FA /* PhoneGapBuildSettings.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = PhoneGapBuildSettings.xcconfig; sourceTree = "<group>"; };
+               308D052E1370CCF300D202BF /* icon-72.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-72.png"; sourceTree = "<group>"; };
+               308D052F1370CCF300D202BF /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon.png; sourceTree = "<group>"; };
+               308D05301370CCF300D202BF /* icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon@2x.png"; sourceTree = "<group>"; };
+               308D05321370CCF300D202BF /* Default-Landscape.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape.png"; sourceTree = "<group>"; };
+               308D05331370CCF300D202BF /* Default-Portrait.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait.png"; sourceTree = "<group>"; };
+               308D05341370CCF300D202BF /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = "<group>"; };
+               308D05351370CCF300D202BF /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = "<group>"; };
+               30E1352610E2C1420031B30D /* PhoneGap.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = PhoneGap.plist; sourceTree = "<group>"; };
+               30E5649113A7FCAF007403D8 /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; };
+               32CA4F630368D1EE00C91783 /* wl_mobi-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "wl_mobi-Prefix.pch"; sourceTree = "<group>"; };
+               8D1107310486CEB800E47090 /* wl_mobi-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "wl_mobi-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = "<group>"; };
+               98B602E3144F0C4200E746E6 /* NativeControls.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NativeControls.h; sourceTree = "<group>"; };
+               98B602E4144F0C4200E746E6 /* NativeControls.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = NativeControls.js; sourceTree = "<group>"; };
+               98B602E5144F0C4200E746E6 /* NativeControls.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NativeControls.m; sourceTree = "<group>"; };
+               98DF335B144EE40D001381F0 /* www */ = {isa = PBXFileReference; explicitFileType = folder; path = www; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+               1D60588F0D05DD3D006BFB54 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               301BF552109A68D80062928A /* libPhoneGap.a in Frameworks */,
+                               1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */,
+                               1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */,
+                               288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */,
+                               301BF5B5109A6A2B0062928A /* AddressBook.framework in Frameworks */,
+                               301BF5B7109A6A2B0062928A /* AddressBookUI.framework in Frameworks */,
+                               301BF5B9109A6A2B0062928A /* AudioToolbox.framework in Frameworks */,
+                               301BF5BB109A6A2B0062928A /* AVFoundation.framework in Frameworks */,
+                               301BF5BD109A6A2B0062928A /* CFNetwork.framework in Frameworks */,
+                               301BF5BF109A6A2B0062928A /* CoreLocation.framework in Frameworks */,
+                               301BF5C1109A6A2B0062928A /* MediaPlayer.framework in Frameworks */,
+                               301BF5C3109A6A2B0062928A /* QuartzCore.framework in Frameworks */,
+                               301BF5C5109A6A2B0062928A /* SystemConfiguration.framework in Frameworks */,
+                               305D5FD1115AB8F900A74A75 /* MobileCoreServices.framework in Frameworks */,
+                               30E5649213A7FCAF007403D8 /* CoreMedia.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+               080E96DDFE201D6D7F000001 /* Classes */ = {
+                       isa = PBXGroup;
+                       children = (
+                               1D3623240D0F684500981E51 /* AppDelegate.h */,
+                               1D3623250D0F684500981E51 /* AppDelegate.m */,
+                       );
+                       path = Classes;
+                       sourceTree = SOURCE_ROOT;
+               };
+               19C28FACFE9D520D11CA2CBB /* Products */ = {
+                       isa = PBXGroup;
+                       children = (
+                               1D6058910D05DD3D006BFB54 /* Wolne Lektury.app */,
+                       );
+                       name = Products;
+                       sourceTree = "<group>";
+               };
+               29B97314FDCFA39411CA2CEA /* CustomTemplate */ = {
+                       isa = PBXGroup;
+                       children = (
+                               98DF335B144EE40D001381F0 /* www */,
+                               301BF52D109A57CC0062928A /* PhoneGapLib.xcodeproj */,
+                               080E96DDFE201D6D7F000001 /* Classes */,
+                               307C750510C5A3420062BCA9 /* Plugins */,
+                               29B97315FDCFA39411CA2CEA /* Other Sources */,
+                               29B97317FDCFA39411CA2CEA /* Resources */,
+                               29B97323FDCFA39411CA2CEA /* Frameworks */,
+                               19C28FACFE9D520D11CA2CBB /* Products */,
+                       );
+                       name = CustomTemplate;
+                       sourceTree = "<group>";
+               };
+               29B97315FDCFA39411CA2CEA /* Other Sources */ = {
+                       isa = PBXGroup;
+                       children = (
+                               32CA4F630368D1EE00C91783 /* wl_mobi-Prefix.pch */,
+                               29B97316FDCFA39411CA2CEA /* main.m */,
+                       );
+                       name = "Other Sources";
+                       sourceTree = "<group>";
+               };
+               29B97317FDCFA39411CA2CEA /* Resources */ = {
+                       isa = PBXGroup;
+                       children = (
+                               308D052D1370CCF300D202BF /* icons */,
+                               308D05311370CCF300D202BF /* splash */,
+                               30E1352610E2C1420031B30D /* PhoneGap.plist */,
+                               3053AC6E109B7857006FCFE7 /* VERSION */,
+                               8D1107310486CEB800E47090 /* wl_mobi-Info.plist */,
+                               307D28A1123043350040C0FA /* PhoneGapBuildSettings.xcconfig */,
+                       );
+                       name = Resources;
+                       sourceTree = "<group>";
+               };
+               29B97323FDCFA39411CA2CEA /* Frameworks */ = {
+                       isa = PBXGroup;
+                       children = (
+                               1DF5F4DF0D08C38300B7A737 /* UIKit.framework */,
+                               1D30AB110D05D00D00671497 /* Foundation.framework */,
+                               288765FC0DF74451002DB57D /* CoreGraphics.framework */,
+                               301BF5B4109A6A2B0062928A /* AddressBook.framework */,
+                               301BF5B6109A6A2B0062928A /* AddressBookUI.framework */,
+                               301BF5B8109A6A2B0062928A /* AudioToolbox.framework */,
+                               301BF5BA109A6A2B0062928A /* AVFoundation.framework */,
+                               301BF5BC109A6A2B0062928A /* CFNetwork.framework */,
+                               301BF5BE109A6A2B0062928A /* CoreLocation.framework */,
+                               301BF5C0109A6A2B0062928A /* MediaPlayer.framework */,
+                               301BF5C2109A6A2B0062928A /* QuartzCore.framework */,
+                               301BF5C4109A6A2B0062928A /* SystemConfiguration.framework */,
+                               305D5FD0115AB8F900A74A75 /* MobileCoreServices.framework */,
+                               30E5649113A7FCAF007403D8 /* CoreMedia.framework */,
+                       );
+                       name = Frameworks;
+                       sourceTree = "<group>";
+               };
+               301BF52E109A57CC0062928A /* Products */ = {
+                       isa = PBXGroup;
+                       children = (
+                               301BF535109A57CC0062928A /* libPhoneGap.a */,
+                               30E47BC3136F595F00DBB853 /* PhoneGap.framework */,
+                               9879B551144ED51F0012352D /* PhoneGapLibTests.octest */,
+                       );
+                       name = Products;
+                       sourceTree = "<group>";
+               };
+               307C750510C5A3420062BCA9 /* Plugins */ = {
+                       isa = PBXGroup;
+                       children = (
+                               98B602E2144F0C4200E746E6 /* NativeControls */,
+                       );
+                       path = Plugins;
+                       sourceTree = SOURCE_ROOT;
+               };
+               308D052D1370CCF300D202BF /* icons */ = {
+                       isa = PBXGroup;
+                       children = (
+                               308D052E1370CCF300D202BF /* icon-72.png */,
+                               308D052F1370CCF300D202BF /* icon.png */,
+                               308D05301370CCF300D202BF /* icon@2x.png */,
+                       );
+                       name = icons;
+                       path = Resources/icons;
+                       sourceTree = "<group>";
+               };
+               308D05311370CCF300D202BF /* splash */ = {
+                       isa = PBXGroup;
+                       children = (
+                               308D05321370CCF300D202BF /* Default-Landscape.png */,
+                               308D05331370CCF300D202BF /* Default-Portrait.png */,
+                               308D05341370CCF300D202BF /* Default.png */,
+                               308D05351370CCF300D202BF /* Default@2x.png */,
+                       );
+                       name = splash;
+                       path = Resources/splash;
+                       sourceTree = "<group>";
+               };
+               98B602E2144F0C4200E746E6 /* NativeControls */ = {
+                       isa = PBXGroup;
+                       children = (
+                               98B602E3144F0C4200E746E6 /* NativeControls.h */,
+                               98B602E4144F0C4200E746E6 /* NativeControls.js */,
+                               98B602E5144F0C4200E746E6 /* NativeControls.m */,
+                       );
+                       path = NativeControls;
+                       sourceTree = "<group>";
+               };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+               1D6058900D05DD3D006BFB54 /* Wolne Lektury */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "Wolne Lektury" */;
+                       buildPhases = (
+                               304B58A110DAC018002A0835 /* Touch www folder */,
+                               1D60588D0D05DD3D006BFB54 /* Resources */,
+                               98DF3300144EE25A001381F0 /* CopyFiles */,
+                               1D60588E0D05DD3D006BFB54 /* Sources */,
+                               1D60588F0D05DD3D006BFB54 /* Frameworks */,
+                               98DF3307144EE28A001381F0 /* CopyFiles */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                               301BF551109A68C00062928A /* PBXTargetDependency */,
+                       );
+                       name = "Wolne Lektury";
+                       productName = "wl-mobi";
+                       productReference = 1D6058910D05DD3D006BFB54 /* Wolne Lektury.app */;
+                       productType = "com.apple.product-type.application";
+               };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+               29B97313FDCFA39411CA2CEA /* Project object */ = {
+                       isa = PBXProject;
+                       buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Wolne Lektury" */;
+                       compatibilityVersion = "Xcode 3.1";
+                       developmentRegion = English;
+                       hasScannedForEncodings = 1;
+                       knownRegions = (
+                               English,
+                               Japanese,
+                               French,
+                               German,
+                               en,
+                               es,
+                       );
+                       mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */;
+                       projectDirPath = "";
+                       projectReferences = (
+                               {
+                                       ProductGroup = 301BF52E109A57CC0062928A /* Products */;
+                                       ProjectRef = 301BF52D109A57CC0062928A /* PhoneGapLib.xcodeproj */;
+                               },
+                       );
+                       projectRoot = "";
+                       targets = (
+                               1D6058900D05DD3D006BFB54 /* Wolne Lektury */,
+                       );
+               };
+/* End PBXProject section */
+
+/* Begin PBXReferenceProxy section */
+               301BF535109A57CC0062928A /* libPhoneGap.a */ = {
+                       isa = PBXReferenceProxy;
+                       fileType = archive.ar;
+                       path = libPhoneGap.a;
+                       remoteRef = 301BF534109A57CC0062928A /* PBXContainerItemProxy */;
+                       sourceTree = BUILT_PRODUCTS_DIR;
+               };
+               30E47BC3136F595F00DBB853 /* PhoneGap.framework */ = {
+                       isa = PBXReferenceProxy;
+                       fileType = wrapper.cfbundle;
+                       path = PhoneGap.framework;
+                       remoteRef = 30E47BC2136F595F00DBB853 /* PBXContainerItemProxy */;
+                       sourceTree = BUILT_PRODUCTS_DIR;
+               };
+               9879B551144ED51F0012352D /* PhoneGapLibTests.octest */ = {
+                       isa = PBXReferenceProxy;
+                       fileType = wrapper.cfbundle;
+                       path = PhoneGapLibTests.octest;
+                       remoteRef = 9879B550144ED51F0012352D /* PBXContainerItemProxy */;
+                       sourceTree = BUILT_PRODUCTS_DIR;
+               };
+/* End PBXReferenceProxy section */
+
+/* Begin PBXResourcesBuildPhase section */
+               1D60588D0D05DD3D006BFB54 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               3053AC6F109B7857006FCFE7 /* VERSION in Resources */,
+                               30E1352710E2C1420031B30D /* PhoneGap.plist in Resources */,
+                               307D28A2123043360040C0FA /* PhoneGapBuildSettings.xcconfig in Resources */,
+                               308D05371370CCF300D202BF /* icon-72.png in Resources */,
+                               308D05381370CCF300D202BF /* icon.png in Resources */,
+                               308D05391370CCF300D202BF /* icon@2x.png in Resources */,
+                               308D053A1370CCF300D202BF /* Default-Landscape.png in Resources */,
+                               308D053B1370CCF300D202BF /* Default-Portrait.png in Resources */,
+                               308D053C1370CCF300D202BF /* Default.png in Resources */,
+                               308D053D1370CCF300D202BF /* Default@2x.png in Resources */,
+                               98DF3370144EE40D001381F0 /* www in Resources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+               304B58A110DAC018002A0835 /* Touch www folder */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       inputPaths = (
+                       );
+                       name = "Touch www folder";
+                       outputPaths = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+                       shellPath = /bin/sh;
+                       shellScript = "touch -cm ${PROJECT_DIR}/www";
+               };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+               1D60588E0D05DD3D006BFB54 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               1D60589B0D05DD56006BFB54 /* main.m in Sources */,
+                               1D3623260D0F684500981E51 /* AppDelegate.m in Sources */,
+                               98B602E6144F0C4200E746E6 /* NativeControls.js in Sources */,
+                               98B602E7144F0C4200E746E6 /* NativeControls.m in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+               301BF551109A68C00062928A /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = PhoneGapLib;
+                       targetProxy = 301BF550109A68C00062928A /* PBXContainerItemProxy */;
+               };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+               1D6058940D05DD3E006BFB54 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               COPY_PHASE_STRIP = NO;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PRECOMPILE_PREFIX_HEADER = YES;
+                               GCC_PREFIX_HEADER = "wl_mobi-Prefix.pch";
+                               INFOPLIST_FILE = "wl_mobi-Info.plist";
+                               IPHONEOS_DEPLOYMENT_TARGET = 3.0;
+                               PRODUCT_NAME = "Wolne Lektury";
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                       };
+                       name = Debug;
+               };
+               1D6058950D05DD3E006BFB54 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               COPY_PHASE_STRIP = YES;
+                               GCC_PRECOMPILE_PREFIX_HEADER = YES;
+                               GCC_PREFIX_HEADER = "wl_mobi-Prefix.pch";
+                               INFOPLIST_FILE = "wl_mobi-Info.plist";
+                               IPHONEOS_DEPLOYMENT_TARGET = 3.0;
+                               PRODUCT_NAME = "Wolne Lektury";
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                       };
+                       name = Release;
+               };
+               C01FCF4F08A954540054247B /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 307D28A1123043350040C0FA /* PhoneGapBuildSettings.xcconfig */;
+                       buildSettings = {
+                               ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+                               GCC_C_LANGUAGE_STANDARD = c99;
+                               GCC_VERSION = com.apple.compilers.llvmgcc42;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
+                               IPHONEOS_DEPLOYMENT_TARGET = 3.0;
+                               OTHER_LDFLAGS = (
+                                       "-weak_framework",
+                                       UIKit,
+                                       "-weak_framework",
+                                       AVFoundation,
+                                       "-weak_framework",
+                                       CoreMedia,
+                                       "-weak_library",
+                                       /usr/lib/libSystem.B.dylib,
+                                       "-all_load",
+                                       "-Obj-C",
+                               );
+                               PREBINDING = NO;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = NO;
+                               USER_HEADER_SEARCH_PATHS = "\"$(PHONEGAPLIB)/Classes/JSON\" \"$(PHONEGAPLIB)/Classes\"";
+                       };
+                       name = Debug;
+               };
+               C01FCF5008A954540054247B /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 307D28A1123043350040C0FA /* PhoneGapBuildSettings.xcconfig */;
+                       buildSettings = {
+                               ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+                               GCC_C_LANGUAGE_STANDARD = c99;
+                               GCC_VERSION = com.apple.compilers.llvmgcc42;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
+                               IPHONEOS_DEPLOYMENT_TARGET = 3.0;
+                               OTHER_LDFLAGS = (
+                                       "-weak_framework",
+                                       UIKit,
+                                       "-weak_framework",
+                                       AVFoundation,
+                                       "-weak_framework",
+                                       CoreMedia,
+                                       "-weak_library",
+                                       /usr/lib/libSystem.B.dylib,
+                                       "-all_load",
+                                       "-Obj-C",
+                               );
+                               PREBINDING = NO;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = NO;
+                               USER_HEADER_SEARCH_PATHS = "\"$(PHONEGAPLIB)/Classes/JSON\" \"$(PHONEGAPLIB)/Classes\"";
+                       };
+                       name = Release;
+               };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+               1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "Wolne Lektury" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               1D6058940D05DD3E006BFB54 /* Debug */,
+                               1D6058950D05DD3E006BFB54 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Wolne Lektury" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               C01FCF4F08A954540054247B /* Debug */,
+                               C01FCF5008A954540054247B /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+/* End XCConfigurationList section */
+       };
+       rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
+}
diff --git a/assets/www/css/book_text.css b/assets/www/css/book_text.css
deleted file mode 100644 (file)
index 9fd9eac..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
- * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
- */
-
-#book-text {
-       font-size: .9em;
-    line-height: 1.5em;
-    margin: 0;
-}
-
-#book-text a {
-    color: blue;
-    text-decoration: none;
-}
-
-.night-mode #book-text a {
-    color: #88f;
-}
-
-
-/* ================================== */
-/* = Header with logo and menu      = */
-/* ================================== */
-#book-text #toc, #themes, #nota_red, #info {
-    display: none;
-}
-
-/* =================================================== */
-/* = Common elements: headings, paragraphs and lines = */
-/* =================================================== */
-#book-text h1 {
-    font-size: 2.5em;
-    margin: 1.5em 0;
-    text-align: center;
-    line-height: 1.5em;
-    font-weight: bold;
-}
-
-#book-text h2 {
-    font-size: 1.7em;
-    margin: 1.5em 0 0;
-    font-weight: bold;
-    line-height: 1.5em;
-}
-
-#book-text h3 {
-    font-size: 1.4em;
-    margin: 1.5em 0 0;
-    font-weight: normal;
-    line-height: 1.5em;
-}
-
-#book-text h4 {
-    font-size: 1em;
-    margin: 1.5em 0 0;
-    line-height: 1.5em;
-}
-
-#book-text p {
-    margin: 0;
-}
-
-/* ======================== */
-/* = Footnotes and themes = */
-/* ======================== */
-#book-text .theme-begin {
-       display:none;
-}
-
-#book-text .annotation {
-    font-style: normal;
-    font-weight: normal;
-    font-size: 12px;
-    padding-left: 2px;
-    position: relative;
-    top: -4px;
-}
-
-#book-text #footnotes {
-    margin-top: 3em;
-}
-
-#book-text #footnotes .annotation {
-    display: block;
-    float: left;
-    width: 2.5em;
-    clear: both;
-}
-
-#book-text #footnotes div {
-    margin: 1.5em 0 0 0;
-}
-
-#book-text #footnotes p, #footnotes ul {
-    margin-left: 2.5em;
-    font-size: 0.875em;
-}
-
-#book-text #footnotes .permalink {
-    font-size: .75em;
-}
-
-#book-text blockquote {
-    font-size: 0.875em;
-    margin: 0 0 0 1em;
-}
-
-/* ============= */
-/* = Numbering = */
-/* ============= */
-#book-text .anchor{display:none;}
-
-/* =================== */
-/* = Custom elements = */
-/* =================== */
-#book-text span.author {
-    font-size: 0.5em;
-    display: block;
-    line-height: 1.5em;
-    margin-bottom: 0.25em;
-}
-
-#book-text span.collection {
-    font-size: 0.375em;
-    display: block;
-    line-height: 1.5em;
-    margin-bottom: -0.25em;
-}
-
-#book-text span.subtitle {
-    font-size: 0.5em;
-    display: block;
-    line-height: 1.5em;
-    margin-top: -0.25em;
-}
-
-#book-text span.translator {
-    font-size: 0.375em;
-    display: block;
-    line-height: 1.5em;
-    margin-top: 0.25em;
-}
-
-#book-text div.didaskalia {
-    font-style: italic;
-    margin: 0.5em 0 0 1.5em;
-}
-
-#book-text div.kwestia {
-    margin: 0.5em 0 0;
-}
-
-#book-text div.stanza {
-    margin: 1.5em 0 0;
-}
-
-#book-text div.kwestia div.stanza {
-    margin: 0;
-}
-
-#book-text p.paragraph {
-    text-align: justify;
-    margin: 1.5em 0 0;
-}
-
-#book-text p.motto {
-    text-align: justify;
-    font-style: italic;
-    margin: 1.5em 0 0;
-}
-
-#book-text p.motto_podpis {
-    font-size: 0.875em;
-    text-align: right;
-}
-
-#book-text div.fragment {
-    border-bottom: 0.1em solid #999;
-    padding-bottom: 1.5em;
-}
-
-#book-text div.note p, div.dedication p, div.note p.paragraph, div.dedication p.paragraph {
-    text-align: right;
-    font-style: italic;
-}
-
-#book-text hr.spacer {
-    height: 3em;
-    visibility: hidden;
-}
-
-#book-text hr.spacer-line {
-    margin: 1.5em 0;
-    border: none;
-    border-bottom: 0.1em solid #000;
-}
-
-#book-text p.spacer-asterisk {
-    padding: 0;
-    margin: 1.5em 0;
-    text-align: center;
-}
-
-#book-text div.person-list ol {
-    list-style: none;
-    padding: 0 0 0 1.5em;
-}
-
-#book-text p.place-and-time {
-    font-style: italic;
-}
-
-#book-text em.math, em.foreign-word, em.book-title, em.didaskalia {
-    font-style: italic;
-}
-
-#book-text em.author-emphasis {
-    letter-spacing: 0.1em;
-}
-
-#book-text em.person {
-    font-style: normal;
-    font-variant: small-caps;
-}
-
-#book-text .verse:after {
-    content: "\feff";
-}
diff --git a/assets/www/css/style.css b/assets/www/css/style.css
deleted file mode 100644 (file)
index 89dc0c2..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
- * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
- */
-
-body {
-       padding: 0;
-       margin: 0;
-       background: white;
-       font-family: verdana, arial, helvetica, sans-serif;
-}
-
-.night-mode, .night-mode #search {
-       color: #ddd;
-       background: #222;
-}
-.night-mode a {
-       color: #fff;
-}
-
-.spinner {
-       margin-top: 4em;
-       text-align: center;
-}
-@-webkit-keyframes rotate {
-  from {
-    -webkit-transform: rotate(0deg);
-  }
-  to { 
-    -webkit-transform: rotate(360deg);
-  }
-}
-.spinner img {
-       -webkit-animation-name: rotate;
-       -webkit-animation-duration: 4s;
-       -webkit-animation-iteration-count: 10;
-       -webkit-animation-direction: right;
-       -webkit-animation-timing-function:linear;
-}
-#spinnertext {
-       margin-top: 2em;
-       font-size: .7em;
-}
-
-#cover {
-       width: 100%;
-}
-
-#searchbox {
-       font-size: 1.25em;
-       background: #84bf2a;
-       padding: 5px;
-       display: none;
-}
-#search {
-       border: none;
-       width: 100%;
-       padding: 5px 0 5px 0;
-}
-#swrap {
-       margin-right:85px;
-}
-#searchbutton {
-       width:80px;
-       float:right;
-}
-#content {
-    padding: 10px;
-}
-.button {
-       display: block;
-       background: #ddd;
-    font-size: 1.25em;
-       padding: .5em;
-       margin-bottom: .5em;
-       border-radius: 8px;
-    -webkit-border-radius: 8px;
-       color: #295158;
-}
-.button img {
-       margin-right: .5em;
-       vertical-align: middle;
-}
-.button .sub {
-       font-size: .7em;
-}
-.button .note {
-       font-size: .5em;
-       text-align: right;
-}
-.button img {
-       float:left;
-}
-.button .label {
-       margin-left: 32px;
-}
-
-.button-Book {
-    background: #ccc;
-}
-.button-BookText {
-    background: #ccc;
-}
-.delete {
-       float: right;
-       width: 24px;
-
-       background: #464646;
-    font-size: 1.25em;
-       padding: .5em;
-       margin-bottom: .5em;
-       border-radius: 8px;
-    -webkit-border-radius: 8px;
-       color: white;
-       text-align: center;
-}
-
-h1 .subheader {
-       display:block;
-       font-size: 70%;
-}
-.upper {
-       text-transform: capitalize;
-}
-
-
-.footer {
-       font-size: 0.75em;
-       text-align: center;
-}
-
-.clr {
-       clear: both;
-}
-
-
-.info dt {
-       display: inline;
-       font-weight: bold;
-}
-
-.info dd {
-       display: inline;
-       margin: 0;
-}
-.info img {
-       max-width: 100%;
-}
\ No newline at end of file
diff --git a/assets/www/img/cc-by-sa.png b/assets/www/img/cc-by-sa.png
deleted file mode 100644 (file)
index 12c70b6..0000000
Binary files a/assets/www/img/cc-by-sa.png and /dev/null differ
diff --git a/assets/www/img/cover.jpg b/assets/www/img/cover.jpg
deleted file mode 100644 (file)
index 343c352..0000000
Binary files a/assets/www/img/cover.jpg and /dev/null differ
diff --git a/assets/www/img/icon-Book.png b/assets/www/img/icon-Book.png
deleted file mode 100644 (file)
index 3c18284..0000000
Binary files a/assets/www/img/icon-Book.png and /dev/null differ
diff --git a/assets/www/img/icon-BookText.png b/assets/www/img/icon-BookText.png
deleted file mode 100644 (file)
index 60d38ee..0000000
Binary files a/assets/www/img/icon-BookText.png and /dev/null differ
diff --git a/assets/www/img/icon-Bookmarks.png b/assets/www/img/icon-Bookmarks.png
deleted file mode 100644 (file)
index 4cb6b9c..0000000
Binary files a/assets/www/img/icon-Bookmarks.png and /dev/null differ
diff --git a/assets/www/img/icon-Last.png b/assets/www/img/icon-Last.png
deleted file mode 100644 (file)
index 81d1b46..0000000
Binary files a/assets/www/img/icon-Last.png and /dev/null differ
diff --git a/assets/www/img/icon-Tag.png b/assets/www/img/icon-Tag.png
deleted file mode 100644 (file)
index db88d3a..0000000
Binary files a/assets/www/img/icon-Tag.png and /dev/null differ
diff --git a/assets/www/img/logo-fnp.png b/assets/www/img/logo-fnp.png
deleted file mode 100644 (file)
index 8720140..0000000
Binary files a/assets/www/img/logo-fnp.png and /dev/null differ
diff --git a/assets/www/img/logo-wl.png b/assets/www/img/logo-wl.png
deleted file mode 100644 (file)
index a18e950..0000000
Binary files a/assets/www/img/logo-wl.png and /dev/null differ
diff --git a/assets/www/img/procent.png b/assets/www/img/procent.png
deleted file mode 100644 (file)
index 2e0d73e..0000000
Binary files a/assets/www/img/procent.png and /dev/null differ
diff --git a/assets/www/img/spinner.png b/assets/www/img/spinner.png
deleted file mode 100644 (file)
index bad5a12..0000000
Binary files a/assets/www/img/spinner.png and /dev/null differ
diff --git a/assets/www/index.html b/assets/www/index.html
deleted file mode 100644 (file)
index fe6e17c..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-<!DOCTYPE html>
-<html style='width:100%'>
-<head>
-    <title>Wolne Lektury</title>
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
-    <script type="text/javascript" charset="utf-8" src="js/phonegap-1.0.0.js"></script>
-
-    <script type="text/javascript" charset="utf-8" src="js/dbput.js"></script>
-    <script type="text/javascript" charset="utf-8" src="js/downloader.js"></script>
-    <script type="text/javascript" charset="utf-8" src="js/menuinterface.js"></script>
-
-    <script type="text/javascript" charset="utf-8" src="js/main.js"></script>
-    <script type="text/javascript" charset="utf-8" src="js/catalogue.js"></script>
-    <script type="text/javascript" charset="utf-8" src="js/filerepo.js"></script>
-    <script type="text/javascript" charset="utf-8" src="js/history.js"></script>
-    <script type="text/javascript" charset="utf-8" src="js/links.js"></script>
-    <script type="text/javascript" charset="utf-8" src="js/menu.js"></script>
-    <script type="text/javascript" charset="utf-8" src="js/view.js"></script>
-
-    <link rel="stylesheet" type="style/css" href="css/style.css" />
-    <link rel="stylesheet" type="style/css" href="css/book_text.css" />
-</head>
-<body onload='onLoad()' style='width:100%'>
-
-
-<div id="searchbox">
-    <form onsubmit="return View.search();">
-    <input id="searchbutton" type="submit" value="Szukaj" />
-    <div id="swrap"><input id="search" /></div>
-    </form>
-</div>
-
-<img id="cover" src="img/cover.jpg" />
-<div id="content">
-<p class='footer'>Uruchamianie…</p>
-</div>
-
-</div>
-
-<div id='nothing'></div>
-</body>   
-</html>
\ No newline at end of file
diff --git a/assets/www/js/catalogue.js b/assets/www/js/catalogue.js
deleted file mode 100644 (file)
index 44f8e80..0000000
+++ /dev/null
@@ -1,375 +0,0 @@
-/*
- * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
- * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
- */
-
-var DB_VER = '0.9.15';
-
-var WL_INITIAL = WL + '/media/api/mobile/initial/initial.db';
-var WL_UPDATE = WL + '/api/changes/SINCE.json?book_fields=author,html,parent,parent_number,sort_key,title' +
-               '&tag_fields=books,category,name,sort_key' +
-               '&tag_categories=author,epoch,genre,kind';
-
-
-
-var categories = {'author': 'autor',
-              'epoch': 'epoka', 
-              'genre': 'gatunek', 
-              'kind': 'rodzaj', 
-              'theme': 'motyw'
-              }
-
-// FIXME: htmlescape strings!
-
-
-// for preparing sql statements
-// use like: 
-//   var s = new Sql("INSERT ... '{0}', '{1}' ...";
-//   s.prepare("abc", ...)
-var Sql = function(scheme) {
-       var self = this;
-       self.text = scheme;
-       
-       self.sql_escape = function(term) {
-               return term.toString().replace("'", "''");
-       };
-       
-       self.prepare = function() {
-               var args = arguments;
-               return self.text.replace(/{(\d+)}/g, function(match, number) {
-                       return self.sql_escape(args[parseInt(number)]);
-               });
-       }
-};
-
-
-var Catalogue = new function() {
-       /* API for database */
-
-       var self = this;
-       self.db = null;
-
-       this.init = function(success, error) {
-               console.log('Catalogue.init');
-               
-               self.updateDB(function() {
-                       self.db = window.openDatabase("wolnelektury", "1.0", "WL Catalogue", 1);
-                       if (self.db) {
-                               /*var regexp = {
-                                               onFunctionCall: function(val) {
-                                                       var re = new RegExp(val.getString(0));
-                                                               if (val.getString(1).match(re))
-                                                                       return 1;
-                                                               else
-                                                                       return 0;
-                                               }
-                                       };
-                               self.db.createFunction("REGEXP", 2, regexp);*/
-
-                               success && success();
-                       } else {
-                               error && error('Nie mogę otworzyć bazy danych: ' + err);
-                       }
-                       
-               }, function(err) {
-                       error && error('Błąd migracji: ' + err);
-               });
-       };
-
-       self.sqlSanitize = function(term) {
-               return term.toString().replace("'", "''");
-       };
-
-
-       /* check if DB needs updating and upload a fresh copy, if so */
-       this.updateDB = function(success, error) {
-               var has_ver = window.localStorage.getItem('db_ver');
-               if (has_ver == DB_VER) {
-                       console.log('db ok, skipping')
-                       success && success();
-                       return;
-               }
-
-               var done = function() {
-                       FileRepo.clear();
-                       window.localStorage.setItem('db_ver', DB_VER);
-                       console.log('db updated');
-                       success && success();
-               };
-
-               // db initialize
-               // this is Android-specific for now
-               self.upload_db_android(done, error);
-       };
-
-
-       this.upload_db_android = function(success, error) {
-               console.log('upload db for Android 2.x+');
-
-               var dbname = "wolnelektury";
-               var db = window.openDatabase(dbname, "1.0", "WL Catalogue", 500000);
-               if (db) {
-                       console.log('db created successfully');
-                       DBPut.fetch(WL_INITIAL, function(data) {
-                               console.log('db fetch successful');
-                               success && success();
-                       }, function(data) {
-                               error && error('Błąd podczas pobierania bazy danych: ' + data);
-                       });
-               } else {
-                       error && error('Błąd podczas inicjowania bazy danych: ' + data);
-               }
-       };
-
-
-       this.withState = function(callback) {
-               self.db.transaction(function(tx) {
-                       tx.executeSql("SELECT * FROM state", [], 
-                               function(tx, results) {
-                                       if (results.rows.length) {
-                                               callback(results.rows.item(0));
-                                       }
-                                       else {
-                                               callback({last_checked: 0});
-                                       }
-                               });
-               });
-       };
-
-
-       this.withBook = function(id, callback, error) {
-               console.log('withBook '+id)
-               self.db.transaction(function(tx) {
-                       tx.executeSql("SELECT * FROM book WHERE id="+id, [], 
-                               function(tx, results) {
-                                       if (results.rows.length) {
-                                               callback(results.rows.item(0));
-                                       }
-                                       else {
-                                               error && error();
-                                       }
-                               });
-               });
-       };
-
-       this.withBooks = function(ids, callback) {
-               console.log('withBooks ' + ids)
-               self.db.transaction(function(tx) {
-                       tx.executeSql("SELECT * FROM book WHERE id IN ("+ids+") ORDER BY sort_key", [], 
-                               function(tx, results) {
-                                       var items = [];
-                                       var count = results.rows.length;
-                                       for (var i=0; i<count; ++i) {
-                                               items.push(results.rows.item(i));
-                                       }
-                                       callback(items);
-                               });
-               });
-       };
-
-
-       this.withChildren = function(id, callback) {
-               console.log('withChildren ' + id)
-               self.db.transaction(function(tx) {
-                       tx.executeSql("SELECT * FROM book WHERE parent="+id+" ORDER BY parent_number, sort_key", [], 
-                               function(tx, results) {
-                                       var books = [];
-                                       var count = results.rows.length;
-                                       for (var i=0; i<count; ++i) {
-                                               books.push(results.rows.item(i));
-                                       }
-                                       callback(books);
-                       });
-               });
-       };
-
-       this.withTag = function(id, callback, error) {
-               console.log('withTag '+id)
-               self.db.transaction(function(tx) {
-                       tx.executeSql("SELECT * FROM tag WHERE id="+id, [], 
-                               function(tx, results) {
-                                       if (results.rows.length) {
-                                               callback(results.rows.item(0));
-                                       }
-                                       else {
-                                               error && error();
-                                       }
-                               });
-               });
-       };
-
-       this.withCategory = function(category, callback) {
-               console.log('withCategory ' + category)
-               self.db.transaction(function(tx) {
-                       tx.executeSql("SELECT * FROM tag WHERE category='"+category+"' ORDER BY sort_key", [], 
-                               function(tx, results) {
-                                       var items = [];
-                                       var count = results.rows.length;
-                                       for (var i=0; i<count; ++i)
-                                               items.push(results.rows.item(i));
-                                       callback(items);
-                               });
-               });
-       };
-
-
-       /* takes a query, returns a list of {view,id,label} objects to a callback */
-       this.withSearch = function(term, callback) {
-               console.log('searching...');
-               term =  term.replace(/^\s+|\s+$/g, '') ;
-               var found = [];
-
-               function booksFound(tx, results) {
-                       var len = results.rows.length;
-                       console.log('found books: ' + len);
-                       for (var i=0; i<len; i++) {
-                               var item = results.rows.item(i);
-                               found.push({
-                                       view: "Book",
-                                       item: item
-                               });
-                       }
-               };
-
-               function tagsFound(tx, results) {
-                       var len = results.rows.length;
-                       console.log('found tags: ' + len);
-                       for (var i=0; i<len; i++) {
-                               var item = results.rows.item(i);
-                               found.push({
-                                       view: "Tag",
-                                       item: item
-                               });
-                       }
-                       // TODO error handling
-                       callback(found);
-               };
-
-
-               // FIXME escaping
-               // TODO pliterki, start of the word match
-               self.db.transaction(function(tx) {
-                       sql_term = self.sqlSanitize(term); // this is still insane, % and _
-                       tx.executeSql("SELECT * FROM book WHERE title LIKE '%"+sql_term+"%' ORDER BY sort_key LIMIT 10", [],
-                       //tx.executeSql("SELECT * FROM book WHERE title REGEXP '.*"+sql_term+".*' ORDER BY sort_key", [],
-                               function(tx, results) {
-                                       // save the books
-                                       booksFound(tx, results);
-                                       // and proceed to tags
-                                       tx.executeSql("SELECT * FROM tag WHERE name LIKE '%"+sql_term+"%' ORDER BY sort_key LIMIT 10",
-                                                       [], tagsFound);
-                               },
-                               function(err) {
-                                       console.log('ERROR:search: '+err.code);
-                                       callback([]);
-                               });
-               });
-       };
-
-       self.chainSqls = function(sqls, success, error) {
-               self.db.transaction(function(tx) {
-                       var do_next = function() {
-                               if (sqls.length) {
-                                       var sql = sqls.shift();
-                                       console.log(sql);
-                                       tx.executeSql(sql, [], do_next, error);
-                               }
-                               else {
-                                       success && success();
-                               }
-                       }
-                       do_next();
-               });
-       };
-
-
-       self.update = function(data, success, error) {
-               var addBookSql = new Sql("\
-                       INSERT OR REPLACE INTO book \
-                               (id, title, html_file,  html_file_size, parent, parent_number, sort_key, pretty_size, authors) \
-                       VALUES \
-                               ('{0}', '{1}', '{2}', '{3}', '{4}', '{5}', '{6}', '{7}', '{8}')");
-               var addTagSql = new Sql("INSERT OR REPLACE INTO tag (id, category, name, sort_key, books) VALUES ('{0}', '{1}', '{2}', '{3}', '{4}')");
-
-               var sqls = [];
-
-               if (data.deleted) {
-                       for (i in data.deleted.books) {
-                               var book_id = data.deleted.books[i];
-                               sqls.push("DELETE FROM book WHERE id=" + book_id);
-                               FileRepo.deleteIfExists(book_id);
-                       }
-
-                       for (i in data.deleted.tags) {
-                               var tag_id = data.deleted.tags[i];
-                               sqls.push("DELETE FROM tag WHERE id=" + tag_id);
-                       }
-               }
-
-               if (data.updated) {
-                       for (i in data.updated.books) {
-                               var book = data.updated.books[i];
-                               if (!book.html) book.html = {};
-                               if (!book.html.url) book.html.url = '';
-                               if (!book.html.size) book.html.size = '';
-                               if (!book.parent) book.parent = '';
-                               if (!book.parent_number) book.parent_number = '';
-                               var pretty_size = prettySize(book.html.size);
-                               sqls.push(addBookSql.prepare(
-                                       book.id, book.title, book.html.url, book.html.size,
-                                       book.parent, book.parent_number, book.sort_key, pretty_size, book.author
-                               ));
-                               FileRepo.deleteIfExists(book.id);
-                       }
-
-                       for (i in data.updated.tags) {
-                               var tag = data.updated.tags[i];
-                               var category = categories[tag.category];
-                               var books = tag.books.join(',');
-                               sqls.push(addTagSql.prepare(tag.id, category, tag.name, tag.sort_key, books));
-                       }
-               }
-
-               sqls.push("UPDATE state SET last_checked=" + data.time_checked);
-
-               self.chainSqls(sqls, success, error);
-       };
-
-
-       this.sync = function(success, error) {
-               self.withState(function(state) {
-                       var url = WL_UPDATE.replace("SINCE", state.last_checked); 
-                       console.log('sync: ' + url);
-                       var xhr = new XMLHttpRequest();
-                       xhr.open("GET", url);
-                       xhr.onload = function() {
-                               console.log('sync: fetched by ajax: ' + url);                   
-                               self.update(JSON.parse(xhr.responseText), success, error);
-                       }
-                       xhr.onerror = function(e) {
-                               error && error("Błąd aktualizacji bazy danych." + e);
-                       }
-                       xhr.send();
-               });
-       };
-
-       this.updateLocal = function() {
-               FileRepo.withLocal(function(local) {
-                       self.db.transaction(function(tx) {
-                               tx.executeSql("UPDATE book SET _local=0", [], function(tx, results) {
-                                       ll = local.length;
-                                       var ids = [];
-                                       for (var i = 0; i < ll; i ++) {
-                                               ids.push(local[i].name);
-                                       }
-                                       ids = ids.join(',');
-                                       tx.executeSql("UPDATE book SET _local=1 where id in ("+ids+")"); 
-                               });
-                       });
-               }, function() {
-                       self.db.transaction(function(tx) {
-                               tx.executeSql("UPDATE book SET _local=0");
-                       });
-               });
-       };
-}
diff --git a/assets/www/js/dbput.js b/assets/www/js/dbput.js
deleted file mode 100644 (file)
index 457f613..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
- * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
- */
-
-/**
- *  
- * @return Object literal singleton instance of DBPut
- */
-var DBPut = { 
-       /**
-     * @param asset Path to the asset (relative to assets dir)
-     * @param target Path to DB file (relative to app db files dir)
-     * @param overwrite
-     * @param win Success callback
-     * @param fail Error callback
-     */
-       put: function(asset, target, overwrite, win, fail) {
-               if (overwrite==false) overwrite="false";
-               else overwrite="true";
-               return PhoneGap.exec(
-                       win, 
-                       fail, 
-                       "DBPut", 
-                       "put", 
-                       [asset, target, overwrite]
-               );
-       },
-       fetch: function(url, win, fail) {
-               return PhoneGap.exec(
-                       win, 
-                       fail, 
-                       "DBPut", 
-                       "fetch", 
-                       [url]
-               );
-       }
-};
-
diff --git a/assets/www/js/downloader.js b/assets/www/js/downloader.js
deleted file mode 100644 (file)
index 15edf30..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- *  
- * @return Object literal singleton instance of Downloader
- */
-var Downloader = { 
-       /**
-     * @param fileUrl
-     * @param dirName
-     * @param fileName
-     * @param overwrite
-     * @param win
-     * @param fail
-     */
-       downloadFile: function(fileUrl,dirName,fileName,overwrite,win,fail) {
-               if(overwrite==false) overwrite="false";
-               else overwrite="true";
-               return PhoneGap.exec(win, fail, "Downloader", "downloadFile", [fileUrl,dirName,fileName,overwrite]);
-       }
-};
diff --git a/assets/www/js/filerepo.js b/assets/www/js/filerepo.js
deleted file mode 100644 (file)
index 8ff5a6a..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
- * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
- */
-
-var FileRepo = new function() {
-       /* API for files repository */
-       var self = this;
-       this.root = null;
-
-       this.init = function(success, error) {
-               self.initRoot(success);
-       };
-
-       this.initRoot = function(success) {
-               // fs size is irrelevant, PERSISTENT is futile (on Android, at least)
-               window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, function(fs) {
-                       console.log('local fs found: ' + fs.root.fullPath);
-                       self.root = fs.root;
-                       success && success();
-               }, function() {
-                       console.log('local fs not found');
-                       success && success();
-               });
-       };
-
-
-       this.withLocalHtml = function(book_id, success, error) {
-               console.log('info:withLocalHtml: id:' + book_id);
-               View.spinner('Otwieranie treści utworu');
-               if (!self.root)
-                       error && error('info:withLocalHtml: no local html: no usable filesystem');
-
-               var url = "file://" + self.root.fullPath + "/html/" + book_id;
-               console.log('info:withLocalHtml: local ajax: ' + url);
-               var xhr = new XMLHttpRequest();
-               xhr.open('GET', url, true);
-               xhr.onload = function() {
-                       console.log('info:withLocalHtml: fetched by local ajax: ' + url);
-                       success && success(xhr.responseText);
-               }
-               xhr.onerror = error;
-               xhr.send();
-       };
-
-
-       this.withLocal = function(win, fail) {
-               if (self.root) {
-                       self.root.getDirectory('html', {}, function(dir) {
-                               var reader = dir.createReader();
-                               reader.readEntries(win, fail);
-                       });
-               }
-               else {
-                       win && win([]);
-               }
-       }
-
-
-       // downloads HTML file from server, saves it in cache and calls success with file contents
-       this.withHtmlFromServer = function(book_id, success, error) {
-               console.log('info:withHtmlFromServer: id:' + book_id);
-               // read file from WL
-               Catalogue.withBook(book_id, function(book) {
-                       var url = WL + book.html_file;
-                       console.log('info:withHtmlFromServer: fetching url: ' + url);
-
-                       View.spinner("Pobieranie treści utworu z sieci");
-
-                       if (self.root) {
-                               Downloader.downloadFile(url, self.root.fullPath + "/html/", ""+book_id, true,
-                                       function(data){
-                                               console.log('info:withHtmlFromServer: loaded file from WL');
-                                               self.withLocalHtml(book_id, success, error);
-                                       }, function(data) {
-                                               console.log('error downloading file!')
-                                               error && error("error: " + data);
-                                       });
-                       }
-                       else {
-                               // there's no big fs, so we'll just get the text from AJAX
-                               console.log('info:withHtmlFromServer: ajax: ' + url);
-                               var xhr = new XMLHttpRequest();
-                               xhr.open("GET", url);
-                               xhr.onload = function() {
-                                       console.log('info:withHtmlFromServer: fetched by ajax: ' + url);
-                                       success && success(xhr.responseText);
-                               }
-                               xhr.onerror = function() {
-                                       console.log('error downloading file!')
-                                       error && error("error: " + data);
-                               }
-                               xhr.send();
-                       }
-               });             
-       };
-       
-       // calls the callback with contents of the HTML file for a given book,
-       // loaded from the server and cached locally
-       this.withHtml = function(id, success, error) {
-               console.log('info:withHtml: id:' + id);
-               self.withLocalHtml(id, success, function() {
-                       self.withHtmlFromServer(id, success, error);
-               });
-       };
-
-
-       this.clear = function() {
-               FileRepo.withLocal(function(local) {
-                       for (i in local) {
-                               local[i].remove();
-                       }
-               });
-       };
-
-
-       this.deleteIfExists = function(id) {
-               if (self.root) {
-                       self.root.getFile('html/' + id, {create: false}, function(f) {
-                               f.remove();
-                       });
-               }
-       }
-};
diff --git a/assets/www/js/history.js b/assets/www/js/history.js
deleted file mode 100644 (file)
index 1b9f9c6..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
- * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
- */
-
-var History = new function() {
-       var self = this;
-
-       self.init = function(success, error) {
-               console.log('History.init');
-
-               self.viewStack = [];
-               navigator.app.overrideBackbutton(); 
-               document.addEventListener("backbutton", History.goBack, true);
-
-               success && success();
-       };
-
-       self.visit = function(url, offset) {
-               offset = offset || 0;
-               self.viewStack.push(View.current);
-               console.log('History.visit: ' + url);
-               View.enter(url, offset);
-       };
-       
-       self.goBack = function() {
-               if (self.viewStack.length > 0) {
-                       var url = self.viewStack.pop();
-                       console.log('History.goBack: ' + url);
-                       View.enter(url);
-               }
-               else {
-                       console.log('History: exiting');
-                       navigator.app.exitApp();
-               }
-       };
-
-
-       self.lastRead = function() {
-               var last_read = window.localStorage.getItem('History.last_ids');
-               try {
-                       return last_read.split(';');
-               } catch (err) {
-                       return [];
-               }
-       };
-
-       self.addRead = function(id, offset) {
-               id = "" + id; // this should check if int
-               console.log("History.addRead: " + id);
-               var last_read = self.lastRead();
-               var lastly = last_read.indexOf(id);
-               if (lastly != -1) {
-                       last_read.splice(lastly, 1);
-               }
-               while (last_read.length >= 10) {
-                       last_read.pop();
-               }
-               last_read.unshift(id);
-               window.localStorage.setItem('History.last_ids', last_read.join(';'));
-       }
-
-
-       self.bookmarks = function() {
-               var bookmarks = window.localStorage.getItem('History.bookmarks');
-               try {
-                       return JSON.parse(bookmarks) || [];
-               } catch (err) {
-                       return [];
-               }
-       };
-
-       self.addBookmark = function(name) {
-               var id=(new Date).getTime();
-               console.log("History.addBookmark: " + id);
-
-               var bms = self.bookmarks();
-               bms.unshift({
-                       id: id,
-                       name: name,
-                       title: View.currentTitle,
-                       view: View.currentView,
-                       par: View.currentPar,
-                       offset: currentOffset()
-               });
-               window.localStorage.setItem('History.bookmarks', JSON.stringify(bms));
-       }
-
-       self.deleteBookmark = function(id) {
-               console.log("History.deleteBookmark: " + id);
-               var bms = self.bookmarks();
-               for (b in bms) {
-                       if (bms[b].id == id) {
-                               bms.splice(b, 1);
-                       }
-               }
-               window.localStorage.setItem('History.bookmarks', JSON.stringify(bms));
-               View.onBookmarkChange();
-       }
-}
diff --git a/assets/www/js/links.js b/assets/www/js/links.js
deleted file mode 100644 (file)
index 3a7465a..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
- * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
- */
-
-var Links = new function() {
-       var self = this;
-
-       self.href = function(view, par) {
-               return view+"/"+par;
-       };
-
-       self.button = function(view, par, text, offset) {
-               offset = offset || 0;
-               var html = "<div class='button button-"+view+"' onclick='History.visit(\"" + 
-                                       self.href(view, par).replace(/["']/g, "\\$&") + "\", "+offset+");'>";
-               icon = view;
-               if (icon != 'Book' && icon != 'Bookmarks' && icon != 'BookText' && 
-                       icon != 'Last' && icon != 'Tag') {
-                   icon = 'Tag';
-               }
-               html += "<img src='img/icon-" + icon + ".png' />";
-               html += "<div class='label'>" + text + "</div>";
-               html += "<div class='clr'></div>";
-               html += "</div>\n";
-               return html; 
-       };
-
-       self.bookLink = function(book) {
-               var target = 'Book';
-               var note = '';
-
-               if (book.html_file) {
-                       // this assumes that either book has a html XOR it has children
-                       target = 'BookText';
-                       note = "<div class='note'>";
-                       if (book._local)
-                               note += 'Pobrane';
-                       else {
-                               note += book.pretty_size;
-                       }
-                       note += "</div>";
-               }
-
-               return self.button(target, book.id,
-                               "<div class='sub'>" + book.authors + "</div>" +
-                               book.title + note);
-       };
-
-       self.deleteButton = function(id) {
-               return "<div class='delete' onClick='History.deleteBookmark(\""+id+"\");'>x</div>";
-       };
-}
diff --git a/assets/www/js/main.js b/assets/www/js/main.js
deleted file mode 100644 (file)
index f3a1eee..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
- * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
- */
-
-var WL = 'http://www.wolnelektury.pl';
-
-
-// disable debugging
-console.log = function(text) {};
-
-
-function onLoad() {
-       console.log('onLoad');
-       document.addEventListener("deviceready", onDeviceReady, false);
-}
-
-function onDeviceReady() {
-       console.log('onDeviceReady');
-       var error = function(err) { alert(err); };
-
-       FileRepo.init(function() {
-               console.log('after FileRepo.init');
-               Catalogue.init(function() {
-                       console.log('after catalogue.init');
-                       History.init(function() {
-                               console.log('after history.init');
-                               View.init(function() {
-                                       Menu.init(function() {
-                                               Catalogue.sync(function() {
-                                                       Catalogue.updateLocal();
-                                               }, error);
-                                       }, error);
-                               }, error);
-                       }, error);
-               }, error);
-       });
-}
-
-
-var currentOffset = function() {
-       var scr = document.body.scrollTop;
-       var h = document.getElementById('nothing').offsetTop;
-       return scr/h;document.getElementById('nothing')
-};
-
-var setOffset = function(offset) {
-       var h = document.getElementById('nothing').offsetTop;
-       scroll(0, h*offset); 
-};
-
-
-var prettySize = function(size) {
-    if (!size) return "";
-    var units = ['B', 'KiB', 'MiB', 'GiB'];
-    size = size;
-    var unit = units.shift();
-    while (size > 1000 && units.length) {
-        size /= 1024;
-        unit = units.shift();
-    }
-    if (size < 10) {
-        return Math.round(size*10)/10 + ' ' + unit;
-    }
-    return Math.round(size) + ' ' + unit;
-};
diff --git a/assets/www/js/menu.js b/assets/www/js/menu.js
deleted file mode 100644 (file)
index 9fea371..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
- * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
- */
-
-var Menu = new function() {
-       var self = this;
-       var infoView = "ProjectInfo";
-
-       self.init = function() {
-               window.MenuInterface.setNightMode(View.getNightMode());
-       };
-
-       self.start = function() {
-               History.visit('');
-       };
-
-       self.info = function() {
-               History.visit(self.infoView + '/' + View.currentPar);
-       };
-
-       self.bookmark = function() {
-               var name = prompt('Nazwa zakładki');
-               if (name != null)
-                       History.addBookmark(name);
-       };
-
-       self.toggleNightMode = function() {
-               View.toggleNightMode();
-               window.MenuInterface.setNightMode(View.getNightMode());
-       };
-
-       self.setInfoButton = function(view, label, enabled) {
-               self.infoView = view;
-               window.MenuInterface.setInfoButton(label, enabled);
-       };
-}
diff --git a/assets/www/js/menuinterface.js b/assets/www/js/menuinterface.js
deleted file mode 100644 (file)
index 596f859..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
- * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
- */
-
-/**
- *  
- * @return Object literal singleton instance of MenuInterface
- */
-var MenuInterface = { 
-       /**
-     * @param asset Path to the asset (relative to assets dir)
-     * @param target Path to DB file (relative to app db files dir)
-     * @param overwrite
-     * @param win Success callback
-     * @param fail Error callback
-     */
-       setInfoButton: function(label, enabled, win, fail) {
-               if (enabled == false) enabled = "false";
-               else enabled = "true";
-               return PhoneGap.exec(
-                       win, 
-                       fail, 
-                       "MenuInterface", 
-                       "setInfoButton", 
-                       [label, enabled]
-               );
-       },
-       setNightMode: function(enabled, win, fail) {
-               if (enabled == false) enabled = "false";
-               else enabled = "true";
-               return PhoneGap.exec(
-                       win, 
-                       fail, 
-                       "MenuInterface", 
-                       "setNightMode", 
-                       [enabled]
-               );
-       },
-};
-
diff --git a/assets/www/js/phonegap-1.0.0.js b/assets/www/js/phonegap-1.0.0.js
deleted file mode 100644 (file)
index 751c52c..0000000
+++ /dev/null
@@ -1,4367 +0,0 @@
-/*
- * PhoneGap is available under *either* the terms of the modified BSD license *or* the
- * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- *
- * Copyright (c) 2005-2010, Nitobi Software Inc.
- * Copyright (c) 2010-2011, IBM Corporation
- */
-
-if (typeof PhoneGap === "undefined") {
-
-/**
- * The order of events during page load and PhoneGap startup is as follows:
- *
- * onDOMContentLoaded         Internal event that is received when the web page is loaded and parsed.
- * window.onload              Body onload event.
- * onNativeReady              Internal event that indicates the PhoneGap native side is ready.
- * onPhoneGapInit             Internal event that kicks off creation of all PhoneGap JavaScript objects (runs constructors).
- * onPhoneGapReady            Internal event fired when all PhoneGap JavaScript objects have been created
- * onPhoneGapInfoReady        Internal event fired when device properties are available
- * onDeviceReady              User event fired to indicate that PhoneGap is ready
- * onResume                   User event fired to indicate a start/resume lifecycle event
- * onPause                    User event fired to indicate a pause lifecycle event
- * onDestroy                  Internal event fired when app is being destroyed (User should use window.onunload event, not this one).
- *
- * The only PhoneGap events that user code should register for are:
- *      onDeviceReady
- *      onResume
- *
- * Listeners can be registered as:
- *      document.addEventListener("deviceready", myDeviceReadyListener, false);
- *      document.addEventListener("resume", myResumeListener, false);
- *      document.addEventListener("pause", myPauseListener, false);
- */
-
-if (typeof(DeviceInfo) !== 'object') {
-    var DeviceInfo = {};
-}
-
-/**
- * This represents the PhoneGap API itself, and provides a global namespace for accessing
- * information about the state of PhoneGap.
- * @class
- */
-var PhoneGap = {
-    queue: {
-        ready: true,
-        commands: [],
-        timer: null
-    }
-};
-
-/**
- * List of resource files loaded by PhoneGap.
- * This is used to ensure JS and other files are loaded only once.
- */
-PhoneGap.resources = {base: true};
-
-/**
- * Determine if resource has been loaded by PhoneGap
- *
- * @param name
- * @return
- */
-PhoneGap.hasResource = function(name) {
-    return PhoneGap.resources[name];
-};
-
-/**
- * Add a resource to list of loaded resources by PhoneGap
- *
- * @param name
- */
-PhoneGap.addResource = function(name) {
-    PhoneGap.resources[name] = true;
-};
-
-/**
- * Custom pub-sub channel that can have functions subscribed to it
- * @constructor
- */
-PhoneGap.Channel = function (type)
-{
-    this.type = type;
-    this.handlers = {};
-    this.guid = 0;
-    this.fired = false;
-    this.enabled = true;
-};
-
-/**
- * Subscribes the given function to the channel. Any time that
- * Channel.fire is called so too will the function.
- * Optionally specify an execution context for the function
- * and a guid that can be used to stop subscribing to the channel.
- * Returns the guid.
- */
-PhoneGap.Channel.prototype.subscribe = function(f, c, g) {
-    // need a function to call
-    if (f === null) { return; }
-
-    var func = f;
-    if (typeof c === "object" && typeof f === "function") { func = PhoneGap.close(c, f); }
-
-    g = g || func.observer_guid || f.observer_guid || this.guid++;
-    func.observer_guid = g;
-    f.observer_guid = g;
-    this.handlers[g] = func;
-    return g;
-};
-
-/**
- * Like subscribe but the function is only called once and then it
- * auto-unsubscribes itself.
- */
-PhoneGap.Channel.prototype.subscribeOnce = function(f, c) {
-    var g = null;
-    var _this = this;
-    var m = function() {
-        f.apply(c || null, arguments);
-        _this.unsubscribe(g);
-    };
-    if (this.fired) {
-        if (typeof c === "object" && typeof f === "function") { f = PhoneGap.close(c, f); }
-        f.apply(this, this.fireArgs);
-    } else {
-        g = this.subscribe(m);
-    }
-    return g;
-};
-
-/**
- * Unsubscribes the function with the given guid from the channel.
- */
-PhoneGap.Channel.prototype.unsubscribe = function(g) {
-    if (typeof g === "function") { g = g.observer_guid; }
-    this.handlers[g] = null;
-    delete this.handlers[g];
-};
-
-/**
- * Calls all functions subscribed to this channel.
- */
-PhoneGap.Channel.prototype.fire = function(e) {
-    if (this.enabled) {
-        var fail = false;
-        var item, handler, rv;
-        for (item in this.handlers) {
-            if (this.handlers.hasOwnProperty(item)) {
-                handler = this.handlers[item];
-                if (typeof handler === "function") {
-                    rv = (handler.apply(this, arguments) === false);
-                    fail = fail || rv;
-                }
-            }
-        }
-        this.fired = true;
-        this.fireArgs = arguments;
-        return !fail;
-    }
-    return true;
-};
-
-/**
- * Calls the provided function only after all of the channels specified
- * have been fired.
- */
-PhoneGap.Channel.join = function(h, c) {
-    var i = c.length;
-    var f = function() {
-        if (!(--i)) {
-            h();
-        }
-    };
-    var len = i;
-    var j;
-    for (j=0; j<len; j++) {
-        if (!c[j].fired) {
-            c[j].subscribeOnce(f);
-        }
-        else {
-            i--;
-        }
-    }
-    if (!i) {
-        h();
-    }
-};
-
-/**
- * Boolean flag indicating if the PhoneGap API is available and initialized.
- */ // TODO: Remove this, it is unused here ... -jm
-PhoneGap.available = DeviceInfo.uuid !== undefined;
-
-/**
- * Add an initialization function to a queue that ensures it will run and initialize
- * application constructors only once PhoneGap has been initialized.
- * @param {Function} func The function callback you want run once PhoneGap is initialized
- */
-PhoneGap.addConstructor = function(func) {
-    PhoneGap.onPhoneGapInit.subscribeOnce(function() {
-        try {
-            func();
-        } catch(e) {
-            console.log("Failed to run constructor: " + e);
-        }
-    });
-};
-
-/**
- * Plugins object
- */
-if (!window.plugins) {
-    window.plugins = {};
-}
-
-/**
- * Adds a plugin object to window.plugins.
- * The plugin is accessed using window.plugins.<name>
- *
- * @param name          The plugin name
- * @param obj           The plugin object
- */
-PhoneGap.addPlugin = function(name, obj) {
-    if (!window.plugins[name]) {
-        window.plugins[name] = obj;
-    }
-    else {
-        console.log("Error: Plugin "+name+" already exists.");
-    }
-};
-
-/**
- * onDOMContentLoaded channel is fired when the DOM content
- * of the page has been parsed.
- */
-PhoneGap.onDOMContentLoaded = new PhoneGap.Channel('onDOMContentLoaded');
-
-/**
- * onNativeReady channel is fired when the PhoneGap native code
- * has been initialized.
- */
-PhoneGap.onNativeReady = new PhoneGap.Channel('onNativeReady');
-
-/**
- * onPhoneGapInit channel is fired when the web page is fully loaded and
- * PhoneGap native code has been initialized.
- */
-PhoneGap.onPhoneGapInit = new PhoneGap.Channel('onPhoneGapInit');
-
-/**
- * onPhoneGapReady channel is fired when the JS PhoneGap objects have been created.
- */
-PhoneGap.onPhoneGapReady = new PhoneGap.Channel('onPhoneGapReady');
-
-/**
- * onPhoneGapInfoReady channel is fired when the PhoneGap device properties
- * has been set.
- */
-PhoneGap.onPhoneGapInfoReady = new PhoneGap.Channel('onPhoneGapInfoReady');
-
-/**
- * onPhoneGapConnectionReady channel is fired when the PhoneGap connection properties
- * has been set.
- */
-PhoneGap.onPhoneGapConnectionReady = new PhoneGap.Channel('onPhoneGapConnectionReady');
-
-/**
- * onResume channel is fired when the PhoneGap native code
- * resumes.
- */
-PhoneGap.onResume = new PhoneGap.Channel('onResume');
-
-/**
- * onPause channel is fired when the PhoneGap native code
- * pauses.
- */
-PhoneGap.onPause = new PhoneGap.Channel('onPause');
-
-/**
- * onDestroy channel is fired when the PhoneGap native code
- * is destroyed.  It is used internally.
- * Window.onunload should be used by the user.
- */
-PhoneGap.onDestroy = new PhoneGap.Channel('onDestroy');
-PhoneGap.onDestroy.subscribeOnce(function() {
-    PhoneGap.shuttingDown = true;
-});
-PhoneGap.shuttingDown = false;
-
-// _nativeReady is global variable that the native side can set
-// to signify that the native code is ready. It is a global since
-// it may be called before any PhoneGap JS is ready.
-if (typeof _nativeReady !== 'undefined') { PhoneGap.onNativeReady.fire(); }
-
-/**
- * onDeviceReady is fired only after all PhoneGap objects are created and
- * the device properties are set.
- */
-PhoneGap.onDeviceReady = new PhoneGap.Channel('onDeviceReady');
-
-
-// Array of channels that must fire before "deviceready" is fired
-PhoneGap.deviceReadyChannelsArray = [ PhoneGap.onPhoneGapReady, PhoneGap.onPhoneGapInfoReady, PhoneGap.onPhoneGapConnectionReady];
-
-// Hashtable of user defined channels that must also fire before "deviceready" is fired
-PhoneGap.deviceReadyChannelsMap = {};
-
-/**
- * Indicate that a feature needs to be initialized before it is ready to be used.
- * This holds up PhoneGap's "deviceready" event until the feature has been initialized
- * and PhoneGap.initComplete(feature) is called.
- *
- * @param feature {String}     The unique feature name
- */
-PhoneGap.waitForInitialization = function(feature) {
-    if (feature) {
-        var channel = new PhoneGap.Channel(feature);
-        PhoneGap.deviceReadyChannelsMap[feature] = channel;
-        PhoneGap.deviceReadyChannelsArray.push(channel);
-    }
-};
-
-/**
- * Indicate that initialization code has completed and the feature is ready to be used.
- *
- * @param feature {String}     The unique feature name
- */
-PhoneGap.initializationComplete = function(feature) {
-    var channel = PhoneGap.deviceReadyChannelsMap[feature];
-    if (channel) {
-        channel.fire();
-    }
-};
-
-/**
- * Create all PhoneGap objects once page has fully loaded and native side is ready.
- */
-PhoneGap.Channel.join(function() {
-
-    // Start listening for XHR callbacks
-    setTimeout(function() {
-            if (PhoneGap.UsePolling) {
-                PhoneGap.JSCallbackPolling();
-            }
-            else {
-                var polling = prompt("usePolling", "gap_callbackServer:");
-                PhoneGap.UsePolling = polling;
-                if (polling == "true") {
-                    PhoneGap.UsePolling = true;
-                    PhoneGap.JSCallbackPolling();
-                }
-                else {
-                    PhoneGap.UsePolling = false;
-                    PhoneGap.JSCallback();
-                }
-            }
-        }, 1);
-
-    // Run PhoneGap constructors
-    PhoneGap.onPhoneGapInit.fire();
-
-    // Fire event to notify that all objects are created
-    PhoneGap.onPhoneGapReady.fire();
-
-    // Fire onDeviceReady event once all constructors have run and PhoneGap info has been
-    // received from native side, and any user defined initialization channels.
-    PhoneGap.Channel.join(function() {
-        PhoneGap.onDeviceReady.fire();
-
-        // Fire the onresume event, since first one happens before JavaScript is loaded
-        PhoneGap.onResume.fire();
-    }, PhoneGap.deviceReadyChannelsArray);
-
-}, [ PhoneGap.onDOMContentLoaded, PhoneGap.onNativeReady ]);
-
-// Listen for DOMContentLoaded and notify our channel subscribers
-document.addEventListener('DOMContentLoaded', function() {
-    PhoneGap.onDOMContentLoaded.fire();
-}, false);
-
-// Intercept calls to document.addEventListener and watch for deviceready
-PhoneGap.m_document_addEventListener = document.addEventListener;
-
-document.addEventListener = function(evt, handler, capture) {
-    var e = evt.toLowerCase();
-    if (e === 'deviceready') {
-        PhoneGap.onDeviceReady.subscribeOnce(handler);
-    } else if (e === 'resume') {
-        PhoneGap.onResume.subscribe(handler);
-        if (PhoneGap.onDeviceReady.fired) {
-            PhoneGap.onResume.fire();
-        }
-    } else if (e === 'pause') {
-        PhoneGap.onPause.subscribe(handler);
-    }
-    else {
-        // If subscribing to Android backbutton
-        if (e === 'backbutton') {
-            PhoneGap.exec(null, null, "App", "overrideBackbutton", [true]);
-        }
-
-        PhoneGap.m_document_addEventListener.call(document, evt, handler, capture);
-    }
-};
-
-// Intercept calls to document.removeEventListener and watch for events that
-// are generated by PhoneGap native code
-PhoneGap.m_document_removeEventListener = document.removeEventListener;
-
-document.removeEventListener = function(evt, handler, capture) {
-    var e = evt.toLowerCase();
-
-    // If unsubscribing to Android backbutton
-    if (e === 'backbutton') {
-        PhoneGap.exec(null, null, "App", "overrideBackbutton", [false]);
-    }
-
-    PhoneGap.m_document_removeEventListener.call(document, evt, handler, capture);
-};
-
-/**
- * Method to fire event from native code
- */
-PhoneGap.fireEvent = function(type) {
-    var e = document.createEvent('Events');
-    e.initEvent(type);
-    document.dispatchEvent(e);
-};
-
-/**
- * If JSON not included, use our own stringify. (Android 1.6)
- * The restriction on ours is that it must be an array of simple types.
- *
- * @param args
- * @return {String}
- */
-PhoneGap.stringify = function(args) {
-    if (typeof JSON === "undefined") {
-        var s = "[";
-        var i, type, start, name, nameType, a;
-        for (i = 0; i < args.length; i++) {
-            if (args[i] !== null) {
-                if (i > 0) {
-                    s = s + ",";
-                }
-                type = typeof args[i];
-                if ((type === "number") || (type === "boolean")) {
-                    s = s + args[i];
-                } else if (args[i] instanceof Array) {
-                    s = s + "[" + args[i] + "]";
-                } else if (args[i] instanceof Object) {
-                    start = true;
-                    s = s + '{';
-                    for (name in args[i]) {
-                        if (args[i][name] !== null) {
-                            if (!start) {
-                                s = s + ',';
-                            }
-                            s = s + '"' + name + '":';
-                            nameType = typeof args[i][name];
-                            if ((nameType === "number") || (nameType === "boolean")) {
-                                s = s + args[i][name];
-                            } else if ((typeof args[i][name]) === 'function') {
-                                // don't copy the functions
-                                s = s + '""';
-                            } else if (args[i][name] instanceof Object) {
-                                s = s + PhoneGap.stringify(args[i][name]);
-                            } else {
-                                s = s + '"' + args[i][name] + '"';
-                            }
-                            start = false;
-                        }
-                    }
-                    s = s + '}';
-                } else {
-                    a = args[i].replace(/\\/g, '\\\\');
-                    a = a.replace(/"/g, '\\"');
-                    s = s + '"' + a + '"';
-                }
-            }
-        }
-        s = s + "]";
-        return s;
-    } else {
-        return JSON.stringify(args);
-    }
-};
-
-/**
- * Does a deep clone of the object.
- *
- * @param obj
- * @return {Object}
- */
-PhoneGap.clone = function(obj) {
-    var i, retVal;
-    if(!obj) { 
-        return obj;
-    }
-    
-    if(obj instanceof Array){
-        retVal = [];
-        for(i = 0; i < obj.length; ++i){
-            retVal.push(PhoneGap.clone(obj[i]));
-        }
-        return retVal;
-    }
-    
-    if (typeof obj === "function") {
-        return obj;
-    }
-    
-    if(!(obj instanceof Object)){
-        return obj;
-    }
-    
-    if (obj instanceof Date) {
-        return obj;
-    }
-    
-    retVal = {};
-    for(i in obj){
-        if(!(i in retVal) || retVal[i] !== obj[i]) {
-            retVal[i] = PhoneGap.clone(obj[i]);
-        }
-    }
-    return retVal;
-};
-
-PhoneGap.callbackId = 0;
-PhoneGap.callbacks = {};
-PhoneGap.callbackStatus = {
-    NO_RESULT: 0,
-    OK: 1,
-    CLASS_NOT_FOUND_EXCEPTION: 2,
-    ILLEGAL_ACCESS_EXCEPTION: 3,
-    INSTANTIATION_EXCEPTION: 4,
-    MALFORMED_URL_EXCEPTION: 5,
-    IO_EXCEPTION: 6,
-    INVALID_ACTION: 7,
-    JSON_EXCEPTION: 8,
-    ERROR: 9
-    };
-
-
-/**
- * Execute a PhoneGap command.  It is up to the native side whether this action is synch or async.
- * The native side can return:
- *      Synchronous: PluginResult object as a JSON string
- *      Asynchrounous: Empty string ""
- * If async, the native side will PhoneGap.callbackSuccess or PhoneGap.callbackError,
- * depending upon the result of the action.
- *
- * @param {Function} success    The success callback
- * @param {Function} fail       The fail callback
- * @param {String} service      The name of the service to use
- * @param {String} action       Action to be run in PhoneGap
- * @param {Array.<String>} [args]     Zero or more arguments to pass to the method
- */
-PhoneGap.exec = function(success, fail, service, action, args) {
-    try {
-        var callbackId = service + PhoneGap.callbackId++;
-        if (success || fail) {
-            PhoneGap.callbacks[callbackId] = {success:success, fail:fail};
-        }
-
-        var r = prompt(PhoneGap.stringify(args), "gap:"+PhoneGap.stringify([service, action, callbackId, true]));
-
-        // If a result was returned
-        if (r.length > 0) {
-            eval("var v="+r+";");
-
-            // If status is OK, then return value back to caller
-            if (v.status === PhoneGap.callbackStatus.OK) {
-
-                // If there is a success callback, then call it now with
-                // returned value
-                if (success) {
-                    try {
-                        success(v.message);
-                    } catch (e) {
-                        console.log("Error in success callback: " + callbackId  + " = " + e);
-                    }
-
-                    // Clear callback if not expecting any more results
-                    if (!v.keepCallback) {
-                        delete PhoneGap.callbacks[callbackId];
-                    }
-                }
-                return v.message;
-            }
-
-            // If no result
-            else if (v.status === PhoneGap.callbackStatus.NO_RESULT) {
-
-                // Clear callback if not expecting any more results
-                if (!v.keepCallback) {
-                    delete PhoneGap.callbacks[callbackId];
-                }
-            }
-
-            // If error, then display error
-            else {
-                console.log("Error: Status="+v.status+" Message="+v.message);
-
-                // If there is a fail callback, then call it now with returned value
-                if (fail) {
-                    try {
-                        fail(v.message);
-                    }
-                    catch (e1) {
-                        console.log("Error in error callback: "+callbackId+" = "+e1);
-                    }
-
-                    // Clear callback if not expecting any more results
-                    if (!v.keepCallback) {
-                        delete PhoneGap.callbacks[callbackId];
-                    }
-                }
-                return null;
-            }
-        }
-    } catch (e2) {
-        console.log("Error: "+e2);
-    }
-};
-
-/**
- * Called by native code when returning successful result from an action.
- *
- * @param callbackId
- * @param args
- */
-PhoneGap.callbackSuccess = function(callbackId, args) {
-    if (PhoneGap.callbacks[callbackId]) {
-
-        // If result is to be sent to callback
-        if (args.status === PhoneGap.callbackStatus.OK) {
-            try {
-                if (PhoneGap.callbacks[callbackId].success) {
-                    PhoneGap.callbacks[callbackId].success(args.message);
-                }
-            }
-            catch (e) {
-                console.log("Error in success callback: "+callbackId+" = "+e);
-            }
-        }
-
-        // Clear callback if not expecting any more results
-        if (!args.keepCallback) {
-            delete PhoneGap.callbacks[callbackId];
-        }
-    }
-};
-
-/**
- * Called by native code when returning error result from an action.
- *
- * @param callbackId
- * @param args
- */
-PhoneGap.callbackError = function(callbackId, args) {
-    if (PhoneGap.callbacks[callbackId]) {
-        try {
-            if (PhoneGap.callbacks[callbackId].fail) {
-                PhoneGap.callbacks[callbackId].fail(args.message);
-            }
-        }
-        catch (e) {
-            console.log("Error in error callback: "+callbackId+" = "+e);
-        }
-
-        // Clear callback if not expecting any more results
-        if (!args.keepCallback) {
-            delete PhoneGap.callbacks[callbackId];
-        }
-    }
-};
-
-
-/**
- * Internal function used to dispatch the request to PhoneGap.  It processes the
- * command queue and executes the next command on the list.  If one of the
- * arguments is a JavaScript object, it will be passed on the QueryString of the
- * url, which will be turned into a dictionary on the other end.
- * @private
- */
-// TODO: Is this used?
-PhoneGap.run_command = function() {
-    if (!PhoneGap.available || !PhoneGap.queue.ready) {
-        return;
-    }
-    PhoneGap.queue.ready = false;
-
-    var args = PhoneGap.queue.commands.shift();
-    if (PhoneGap.queue.commands.length === 0) {
-        clearInterval(PhoneGap.queue.timer);
-        PhoneGap.queue.timer = null;
-    }
-
-    var uri = [];
-    var dict = null;
-    var i;
-    for (i = 1; i < args.length; i++) {
-        var arg = args[i];
-        if (arg === undefined || arg === null) {
-            arg = '';
-        }
-        if (typeof(arg) === 'object') {
-            dict = arg;
-        } else {
-            uri.push(encodeURIComponent(arg));
-        }
-    }
-    var url = "gap://" + args[0] + "/" + uri.join("/");
-    if (dict !== null) {
-        var name;
-        var query_args = [];
-        for (name in dict) {
-            if (dict.hasOwnProperty(name) && (typeof (name) === 'string')) {
-                query_args.push(encodeURIComponent(name) + "=" + encodeURIComponent(dict[name]));
-            }
-        }
-        if (query_args.length > 0) {
-            url += "?" + query_args.join("&");
-        }
-    }
-    document.location = url;
-
-};
-
-PhoneGap.JSCallbackPort = null;
-PhoneGap.JSCallbackToken = null;
-
-/**
- * This is only for Android.
- *
- * Internal function that uses XHR to call into PhoneGap Java code and retrieve
- * any JavaScript code that needs to be run.  This is used for callbacks from
- * Java to JavaScript.
- */
-PhoneGap.JSCallback = function() {
-
-    // Exit if shutting down app
-    if (PhoneGap.shuttingDown) {
-        return;
-    }
-
-    // If polling flag was changed, start using polling from now on
-    if (PhoneGap.UsePolling) {
-        PhoneGap.JSCallbackPolling();
-        return;
-    }
-
-    var xmlhttp = new XMLHttpRequest();
-
-    // Callback function when XMLHttpRequest is ready
-    xmlhttp.onreadystatechange=function(){
-        if(xmlhttp.readyState === 4){
-
-            // Exit if shutting down app
-            if (PhoneGap.shuttingDown) {
-                return;
-            }
-
-            // If callback has JavaScript statement to execute
-            if (xmlhttp.status === 200) {
-
-                // Need to url decode the response
-                var msg = decodeURIComponent(xmlhttp.responseText);
-                setTimeout(function() {
-                    try {
-                        var t = eval(msg);
-                    }
-                    catch (e) {
-                        // If we're getting an error here, seeing the message will help in debugging
-                        console.log("JSCallback: Message from Server: " + msg);
-                        console.log("JSCallback Error: "+e);
-                    }
-                }, 1);
-                setTimeout(PhoneGap.JSCallback, 1);
-            }
-
-            // If callback ping (used to keep XHR request from timing out)
-            else if (xmlhttp.status === 404) {
-                setTimeout(PhoneGap.JSCallback, 10);
-            }
-
-            // If security error
-            else if (xmlhttp.status === 403) {
-                console.log("JSCallback Error: Invalid token.  Stopping callbacks.");
-            }
-
-            // If server is stopping
-            else if (xmlhttp.status === 503) {
-                console.log("JSCallback Error: Service unavailable.  Stopping callbacks.");
-            }
-
-            // If request wasn't GET
-            else if (xmlhttp.status === 400) {
-                console.log("JSCallback Error: Bad request.  Stopping callbacks.");
-            }
-
-            // If error, revert to polling
-            else {
-                console.log("JSCallback Error: Request failed.");
-                PhoneGap.UsePolling = true;
-                PhoneGap.JSCallbackPolling();
-            }
-        }
-    };
-
-    if (PhoneGap.JSCallbackPort === null) {
-        PhoneGap.JSCallbackPort = prompt("getPort", "gap_callbackServer:");
-    }
-    if (PhoneGap.JSCallbackToken === null) {
-        PhoneGap.JSCallbackToken = prompt("getToken", "gap_callbackServer:");
-    }
-    xmlhttp.open("GET", "http://127.0.0.1:"+PhoneGap.JSCallbackPort+"/"+PhoneGap.JSCallbackToken , true);
-    xmlhttp.send();
-};
-
-/**
- * The polling period to use with JSCallbackPolling.
- * This can be changed by the application.  The default is 50ms.
- */
-PhoneGap.JSCallbackPollingPeriod = 50;
-
-/**
- * Flag that can be set by the user to force polling to be used or force XHR to be used.
- */
-PhoneGap.UsePolling = false;    // T=use polling, F=use XHR
-
-/**
- * This is only for Android.
- *
- * Internal function that uses polling to call into PhoneGap Java code and retrieve
- * any JavaScript code that needs to be run.  This is used for callbacks from
- * Java to JavaScript.
- */
-PhoneGap.JSCallbackPolling = function() {
-
-    // Exit if shutting down app
-    if (PhoneGap.shuttingDown) {
-        return;
-    }
-
-    // If polling flag was changed, stop using polling from now on
-    if (!PhoneGap.UsePolling) {
-        PhoneGap.JSCallback();
-        return;
-    }
-
-    var msg = prompt("", "gap_poll:");
-    if (msg) {
-        setTimeout(function() {
-            try {
-                var t = eval(""+msg);
-            }
-            catch (e) {
-                console.log("JSCallbackPolling: Message from Server: " + msg);
-                console.log("JSCallbackPolling Error: "+e);
-            }
-        }, 1);
-        setTimeout(PhoneGap.JSCallbackPolling, 1);
-    }
-    else {
-        setTimeout(PhoneGap.JSCallbackPolling, PhoneGap.JSCallbackPollingPeriod);
-    }
-};
-
-/**
- * Create a UUID
- *
- * @return {String}
- */
-PhoneGap.createUUID = function() {
-    return PhoneGap.UUIDcreatePart(4) + '-' +
-        PhoneGap.UUIDcreatePart(2) + '-' +
-        PhoneGap.UUIDcreatePart(2) + '-' +
-        PhoneGap.UUIDcreatePart(2) + '-' +
-        PhoneGap.UUIDcreatePart(6);
-};
-
-PhoneGap.UUIDcreatePart = function(length) {
-    var uuidpart = "";
-    var i, uuidchar;
-    for (i=0; i<length; i++) {
-        uuidchar = parseInt((Math.random() * 256),0).toString(16);
-        if (uuidchar.length === 1) {
-            uuidchar = "0" + uuidchar;
-        }
-        uuidpart += uuidchar;
-    }
-    return uuidpart;
-};
-
-PhoneGap.close = function(context, func, params) {
-    if (typeof params === 'undefined') {
-        return function() {
-            return func.apply(context, arguments);
-        };
-    } else {
-        return function() {
-            return func.apply(context, params);
-        };
-    }
-};
-
-/**
- * Load a JavaScript file after page has loaded.
- *
- * @param {String} jsfile               The url of the JavaScript file to load.
- * @param {Function} successCallback    The callback to call when the file has been loaded.
- */
-PhoneGap.includeJavascript = function(jsfile, successCallback) {
-    var id = document.getElementsByTagName("head")[0];
-    var el = document.createElement('script');
-    el.type = 'text/javascript';
-    if (typeof successCallback === 'function') {
-        el.onload = successCallback;
-    }
-    el.src = jsfile;
-    id.appendChild(el);
-};
-
-}
-/*
- * PhoneGap is available under *either* the terms of the modified BSD license *or* the
- * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- *
- * Copyright (c) 2005-2010, Nitobi Software Inc.
- * Copyright (c) 2010-2011, IBM Corporation
- */
-
-if (!PhoneGap.hasResource("accelerometer")) {
-PhoneGap.addResource("accelerometer");
-
-/** @constructor */
-var Acceleration = function(x, y, z) {
-  this.x = x;
-  this.y = y;
-  this.z = z;
-  this.timestamp = new Date().getTime();
-};
-
-/**
- * This class provides access to device accelerometer data.
- * @constructor
- */
-var Accelerometer = function() {
-
-    /**
-     * The last known acceleration.  type=Acceleration()
-     */
-    this.lastAcceleration = null;
-
-    /**
-     * List of accelerometer watch timers
-     */
-    this.timers = {};
-};
-
-Accelerometer.ERROR_MSG = ["Not running", "Starting", "", "Failed to start"];
-
-/**
- * Asynchronously aquires the current acceleration.
- *
- * @param {Function} successCallback    The function to call when the acceleration data is available
- * @param {Function} errorCallback      The function to call when there is an error getting the acceleration data. (OPTIONAL)
- * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL)
- */
-Accelerometer.prototype.getCurrentAcceleration = function(successCallback, errorCallback, options) {
-
-    // successCallback required
-    if (typeof successCallback !== "function") {
-        console.log("Accelerometer Error: successCallback is not a function");
-        return;
-    }
-
-    // errorCallback optional
-    if (errorCallback && (typeof errorCallback !== "function")) {
-        console.log("Accelerometer Error: errorCallback is not a function");
-        return;
-    }
-
-    // Get acceleration
-    PhoneGap.exec(successCallback, errorCallback, "Accelerometer", "getAcceleration", []);
-};
-
-/**
- * Asynchronously aquires the acceleration repeatedly at a given interval.
- *
- * @param {Function} successCallback    The function to call each time the acceleration data is available
- * @param {Function} errorCallback      The function to call when there is an error getting the acceleration data. (OPTIONAL)
- * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL)
- * @return String                       The watch id that must be passed to #clearWatch to stop watching.
- */
-Accelerometer.prototype.watchAcceleration = function(successCallback, errorCallback, options) {
-
-    // Default interval (10 sec)
-    var frequency = (options !== undefined)? options.frequency : 10000;
-
-    // successCallback required
-    if (typeof successCallback !== "function") {
-        console.log("Accelerometer Error: successCallback is not a function");
-        return;
-    }
-
-    // errorCallback optional
-    if (errorCallback && (typeof errorCallback !== "function")) {
-        console.log("Accelerometer Error: errorCallback is not a function");
-        return;
-    }
-
-    // Make sure accelerometer timeout > frequency + 10 sec
-    PhoneGap.exec(
-        function(timeout) {
-            if (timeout < (frequency + 10000)) {
-                PhoneGap.exec(null, null, "Accelerometer", "setTimeout", [frequency + 10000]);
-            }
-        },
-        function(e) { }, "Accelerometer", "getTimeout", []);
-
-    // Start watch timer
-    var id = PhoneGap.createUUID();
-    navigator.accelerometer.timers[id] = setInterval(function() {
-        PhoneGap.exec(successCallback, errorCallback, "Accelerometer", "getAcceleration", []);
-    }, (frequency ? frequency : 1));
-
-    return id;
-};
-
-/**
- * Clears the specified accelerometer watch.
- *
- * @param {String} id       The id of the watch returned from #watchAcceleration.
- */
-Accelerometer.prototype.clearWatch = function(id) {
-
-    // Stop javascript timer & remove from timer list
-    if (id && navigator.accelerometer.timers[id] !== undefined) {
-        clearInterval(navigator.accelerometer.timers[id]);
-        delete navigator.accelerometer.timers[id];
-    }
-};
-
-PhoneGap.addConstructor(function() {
-    if (typeof navigator.accelerometer === "undefined") {
-        navigator.accelerometer = new Accelerometer();
-    }
-});
-}
-
-
-/*
- * PhoneGap is available under *either* the terms of the modified BSD license *or* the
- * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- *
- * Copyright (c) 2005-2010, Nitobi Software Inc.
- * Copyright (c) 2010-2011, IBM Corporation
- */
-
-if (!PhoneGap.hasResource("app")) {
-PhoneGap.addResource("app");
-(function() {
-
-/**
- * Constructor
- * @constructor
- */
-var App = function() {};
-
-/**
- * Clear the resource cache.
- */
-App.prototype.clearCache = function() {
-    PhoneGap.exec(null, null, "App", "clearCache", []);
-};
-
-/**
- * Load the url into the webview.
- *
- * @param url           The URL to load
- * @param props         Properties that can be passed in to the activity:
- *      wait: int                           => wait msec before loading URL
- *      loadingDialog: "Title,Message"      => display a native loading dialog
- *      hideLoadingDialogOnPage: boolean    => hide loadingDialog when page loaded instead of when deviceready event occurs.
- *      loadInWebView: boolean              => cause all links on web page to be loaded into existing web view, instead of being loaded into new browser.
- *      loadUrlTimeoutValue: int            => time in msec to wait before triggering a timeout error
- *      errorUrl: URL                       => URL to load if there's an error loading specified URL with loadUrl().  Should be a local URL such as file:///android_asset/www/error.html");
- *      keepRunning: boolean                => enable app to keep running in background
- *
- * Example:
- *      App app = new App();
- *      app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000});
- */
-App.prototype.loadUrl = function(url, props) {
-    PhoneGap.exec(null, null, "App", "loadUrl", [url, props]);
-};
-
-/**
- * Cancel loadUrl that is waiting to be loaded.
- */
-App.prototype.cancelLoadUrl = function() {
-    PhoneGap.exec(null, null, "App", "cancelLoadUrl", []);
-};
-
-/**
- * Clear web history in this web view.
- * Instead of BACK button loading the previous web page, it will exit the app.
- */
-App.prototype.clearHistory = function() {
-    PhoneGap.exec(null, null, "App", "clearHistory", []);
-};
-
-/**
- * Override the default behavior of the Android back button.
- * If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired.
- *
- * Note: The user should not have to call this method.  Instead, when the user
- *       registers for the "backbutton" event, this is automatically done.
- *
- * @param override             T=override, F=cancel override
- */
-App.prototype.overrideBackbutton = function(override) {
-    PhoneGap.exec(null, null, "App", "overrideBackbutton", [override]);
-};
-
-/**
- * Exit and terminate the application.
- */
-App.prototype.exitApp = function() {
-       return PhoneGap.exec(null, null, "App", "exitApp", []);
-};
-
-PhoneGap.addConstructor(function() {
-    navigator.app = new App();
-});
-}());
-}
-
-
-/*
- * PhoneGap is available under *either* the terms of the modified BSD license *or* the
- * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- *
- * Copyright (c) 2005-2010, Nitobi Software Inc.
- * Copyright (c) 2010-2011, IBM Corporation
- */
-
-if (!PhoneGap.hasResource("camera")) {
-PhoneGap.addResource("camera");
-
-/**
- * This class provides access to the device camera.
- *
- * @constructor
- */
-var Camera = function() {
-    this.successCallback = null;
-    this.errorCallback = null;
-    this.options = null;
-};
-
-/**
- * Format of image that returned from getPicture.
- *
- * Example: navigator.camera.getPicture(success, fail,
- *              { quality: 80,
- *                destinationType: Camera.DestinationType.DATA_URL,
- *                sourceType: Camera.PictureSourceType.PHOTOLIBRARY})
- */
-Camera.DestinationType = {
-    DATA_URL: 0,                // Return base64 encoded string
-    FILE_URI: 1                 // Return file uri (content://media/external/images/media/2 for Android)
-};
-Camera.prototype.DestinationType = Camera.DestinationType;
-
-/**
- * Encoding of image returned from getPicture.
- *
- * Example: navigator.camera.getPicture(success, fail,
- *              { quality: 80,
- *                destinationType: Camera.DestinationType.DATA_URL,
- *                sourceType: Camera.PictureSourceType.CAMERA,
- *                encodingType: Camera.EncodingType.PNG})
-*/
-Camera.EncodingType = {
-    JPEG: 0,                    // Return JPEG encoded image
-    PNG: 1                      // Return PNG encoded image
-};
-Camera.prototype.EncodingType = Camera.EncodingType;
-
-/**
- * Source to getPicture from.
- *
- * Example: navigator.camera.getPicture(success, fail,
- *              { quality: 80,
- *                destinationType: Camera.DestinationType.DATA_URL,
- *                sourceType: Camera.PictureSourceType.PHOTOLIBRARY})
- */
-Camera.PictureSourceType = {
-    PHOTOLIBRARY : 0,           // Choose image from picture library (same as SAVEDPHOTOALBUM for Android)
-    CAMERA : 1,                 // Take picture from camera
-    SAVEDPHOTOALBUM : 2         // Choose image from picture library (same as PHOTOLIBRARY for Android)
-};
-Camera.prototype.PictureSourceType = Camera.PictureSourceType;
-
-/**
- * Gets a picture from source defined by "options.sourceType", and returns the
- * image as defined by the "options.destinationType" option.
-
- * The defaults are sourceType=CAMERA and destinationType=DATA_URL.
- *
- * @param {Function} successCallback
- * @param {Function} errorCallback
- * @param {Object} options
- */
-Camera.prototype.getPicture = function(successCallback, errorCallback, options) {
-
-    // successCallback required
-    if (typeof successCallback !== "function") {
-        console.log("Camera Error: successCallback is not a function");
-        return;
-    }
-
-    // errorCallback optional
-    if (errorCallback && (typeof errorCallback !== "function")) {
-        console.log("Camera Error: errorCallback is not a function");
-        return;
-    }
-
-    this.options = options;
-    var quality = 80;
-    if (options.quality) {
-        quality = this.options.quality;
-    }
-    
-    var maxResolution = 0;
-    if (options.maxResolution) {
-       maxResolution = this.options.maxResolution;
-    }
-    
-    var destinationType = Camera.DestinationType.DATA_URL;
-    if (this.options.destinationType) {
-        destinationType = this.options.destinationType;
-    }
-    var sourceType = Camera.PictureSourceType.CAMERA;
-    if (typeof this.options.sourceType === "number") {
-        sourceType = this.options.sourceType;
-    }
-    var encodingType = Camera.EncodingType.JPEG;
-    if (typeof options.encodingType == "number") {
-        encodingType = this.options.encodingType;
-    }
-    
-    var targetWidth = -1;
-    if (typeof options.targetWidth == "number") {
-        targetWidth = options.targetWidth;
-    } else if (typeof options.targetWidth == "string") {
-        var width = new Number(options.targetWidth);
-        if (isNaN(width) === false) {
-            targetWidth = width.valueOf();
-        }
-    }
-
-    var targetHeight = -1;
-    if (typeof options.targetHeight == "number") {
-        targetHeight = options.targetHeight;
-    } else if (typeof options.targetHeight == "string") {
-        var height = new Number(options.targetHeight);
-        if (isNaN(height) === false) {
-            targetHeight = height.valueOf();
-        }
-    }
-    
-    PhoneGap.exec(successCallback, errorCallback, "Camera", "takePicture", [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType]);
-};
-
-PhoneGap.addConstructor(function() {
-    if (typeof navigator.camera === "undefined") {
-        navigator.camera = new Camera();
-    }
-});
-}
-
-
-/*
- * PhoneGap is available under *either* the terms of the modified BSD license *or* the
- * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- *
- * Copyright (c) 2005-2010, Nitobi Software Inc.
- * Copyright (c) 2010-2011, IBM Corporation
- */
-
-if (!PhoneGap.hasResource("capture")) {
-PhoneGap.addResource("capture");
-       
-/**
- * Represents a single file.
- *
- * name {DOMString} name of the file, without path information
- * fullPath {DOMString} the full path of the file, including the name
- * type {DOMString} mime type
- * lastModifiedDate {Date} last modified date
- * size {Number} size of the file in bytes
- */
-var MediaFile = function(name, fullPath, type, lastModifiedDate, size){
-       this.name = name || null;
-       this.fullPath = fullPath || null;
-       this.type = type || null;
-       this.lastModifiedDate = lastModifiedDate || null;
-       this.size = size || 0;
-};
-
-/**
- * Launch device camera application for recording video(s).
- *
- * @param {Function} successCB
- * @param {Function} errorCB
- */
-MediaFile.prototype.getFormatData = function(successCallback, errorCallback){
-       PhoneGap.exec(successCallback, errorCallback, "Capture", "getFormatData", [this.fullPath, this.type]);
-};
-
-/**
- * MediaFileData encapsulates format information of a media file.
- *
- * @param {DOMString} codecs
- * @param {long} bitrate
- * @param {long} height
- * @param {long} width
- * @param {float} duration
- */
-var MediaFileData = function(codecs, bitrate, height, width, duration){
-       this.codecs = codecs || null;
-       this.bitrate = bitrate || 0;
-       this.height = height || 0;
-       this.width = width || 0;
-       this.duration = duration || 0;
-};
-
-/**
- * The CaptureError interface encapsulates all errors in the Capture API.
- */
-var CaptureError = function(){
-       this.code = null;
-};
-
-// Capture error codes
-CaptureError.CAPTURE_INTERNAL_ERR = 0;
-CaptureError.CAPTURE_APPLICATION_BUSY = 1;
-CaptureError.CAPTURE_INVALID_ARGUMENT = 2;
-CaptureError.CAPTURE_NO_MEDIA_FILES = 3;
-CaptureError.CAPTURE_NOT_SUPPORTED = 20;
-
-/**
- * The Capture interface exposes an interface to the camera and microphone of the hosting device.
- */
-var Capture = function(){
-       this.supportedAudioModes = [];
-       this.supportedImageModes = [];
-       this.supportedVideoModes = [];
-};
-
-/**
- * Launch audio recorder application for recording audio clip(s).
- *
- * @param {Function} successCB
- * @param {Function} errorCB
- * @param {CaptureAudioOptions} options
- */
-Capture.prototype.captureAudio = function(successCallback, errorCallback, options){
-       PhoneGap.exec(successCallback, errorCallback, "Capture", "captureAudio", [options]);
-};
-
-/**
- * Launch camera application for taking image(s).
- *
- * @param {Function} successCB
- * @param {Function} errorCB
- * @param {CaptureImageOptions} options
- */
-Capture.prototype.captureImage = function(successCallback, errorCallback, options){
-       PhoneGap.exec(successCallback, errorCallback, "Capture", "captureImage", [options]);
-};
-
-/**
- * Launch camera application for taking image(s).
- *
- * @param {Function} successCB
- * @param {Function} errorCB
- * @param {CaptureImageOptions} options
- */
-Capture.prototype._castMediaFile = function(pluginResult){
-       var mediaFiles = [];
-       var i;
-       for (i = 0; i < pluginResult.message.length; i++) {
-               var mediaFile = new MediaFile();
-               mediaFile.name = pluginResult.message[i].name;
-               mediaFile.fullPath = pluginResult.message[i].fullPath;
-               mediaFile.type = pluginResult.message[i].type;
-               mediaFile.lastModifiedDate = pluginResult.message[i].lastModifiedDate;
-               mediaFile.size = pluginResult.message[i].size;
-               mediaFiles.push(mediaFile);
-       }
-       pluginResult.message = mediaFiles;
-       return pluginResult;
-};
-
-/**
- * Launch device camera application for recording video(s).
- *
- * @param {Function} successCB
- * @param {Function} errorCB
- * @param {CaptureVideoOptions} options
- */
-Capture.prototype.captureVideo = function(successCallback, errorCallback, options){
-       PhoneGap.exec(successCallback, errorCallback, "Capture", "captureVideo", [options]);
-};
-
-/**
- * Encapsulates a set of parameters that the capture device supports.
- */
-var ConfigurationData = function(){
-       // The ASCII-encoded string in lower case representing the media type. 
-       this.type = null;
-       // The height attribute represents height of the image or video in pixels. 
-       // In the case of a sound clip this attribute has value 0. 
-       this.height = 0;
-       // The width attribute represents width of the image or video in pixels. 
-       // In the case of a sound clip this attribute has value 0
-       this.width = 0;
-};
-
-/**
- * Encapsulates all image capture operation configuration options.
- */
-var CaptureImageOptions = function(){
-       // Upper limit of images user can take. Value must be equal or greater than 1.
-       this.limit = 1;
-       // The selected image mode. Must match with one of the elements in supportedImageModes array.
-       this.mode = null;
-};
-
-/**
- * Encapsulates all video capture operation configuration options.
- */
-var CaptureVideoOptions = function(){
-       // Upper limit of videos user can record. Value must be equal or greater than 1.
-       this.limit = 1;
-       // Maximum duration of a single video clip in seconds.
-       this.duration = 0;
-       // The selected video mode. Must match with one of the elements in supportedVideoModes array.
-       this.mode = null;
-};
-
-/**
- * Encapsulates all audio capture operation configuration options.
- */
-var CaptureAudioOptions = function(){
-       // Upper limit of sound clips user can record. Value must be equal or greater than 1.
-       this.limit = 1;
-       // Maximum duration of a single sound clip in seconds.
-       this.duration = 0;
-       // The selected audio mode. Must match with one of the elements in supportedAudioModes array.
-       this.mode = null;
-};
-
-PhoneGap.addConstructor(function(){
-       if (typeof navigator.device === "undefined") {
-               navigator.device = window.device = new Device();
-       }
-       if (typeof navigator.device.capture === "undefined") {
-               navigator.device.capture = window.device.capture = new Capture();
-       }
-});
-}
-
-/*
- * PhoneGap is available under *either* the terms of the modified BSD license *or* the
- * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- *
- * Copyright (c) 2005-2010, Nitobi Software Inc.
- * Copyright (c) 2010-2011, IBM Corporation
- */
-
-if (!PhoneGap.hasResource("compass")) {
-PhoneGap.addResource("compass");
-
-/**
- * This class provides access to device Compass data.
- * @constructor
- */
-var Compass = function() {
-    /**
-     * The last known Compass position.
-     */
-    this.lastHeading = null;
-
-    /**
-     * List of compass watch timers
-     */
-    this.timers = {};
-};
-
-Compass.ERROR_MSG = ["Not running", "Starting", "", "Failed to start"];
-
-/**
- * Asynchronously aquires the current heading.
- *
- * @param {Function} successCallback The function to call when the heading data is available
- * @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL)
- * @param {PositionOptions} options The options for getting the heading data such as timeout. (OPTIONAL)
- */
-Compass.prototype.getCurrentHeading = function(successCallback, errorCallback, options) {
-
-    // successCallback required
-    if (typeof successCallback !== "function") {
-        console.log("Compass Error: successCallback is not a function");
-        return;
-    }
-
-    // errorCallback optional
-    if (errorCallback && (typeof errorCallback !== "function")) {
-        console.log("Compass Error: errorCallback is not a function");
-        return;
-    }
-
-    // Get heading
-    PhoneGap.exec(successCallback, errorCallback, "Compass", "getHeading", []);
-};
-
-/**
- * Asynchronously aquires the heading repeatedly at a given interval.
- *
- * @param {Function} successCallback    The function to call each time the heading data is available
- * @param {Function} errorCallback      The function to call when there is an error getting the heading data. (OPTIONAL)
- * @param {HeadingOptions} options      The options for getting the heading data such as timeout and the frequency of the watch. (OPTIONAL)
- * @return String                       The watch id that must be passed to #clearWatch to stop watching.
- */
-Compass.prototype.watchHeading= function(successCallback, errorCallback, options) {
-
-    // Default interval (100 msec)
-    var frequency = (options !== undefined) ? options.frequency : 100;
-
-    // successCallback required
-    if (typeof successCallback !== "function") {
-        console.log("Compass Error: successCallback is not a function");
-        return;
-    }
-
-    // errorCallback optional
-    if (errorCallback && (typeof errorCallback !== "function")) {
-        console.log("Compass Error: errorCallback is not a function");
-        return;
-    }
-
-    // Make sure compass timeout > frequency + 10 sec
-    PhoneGap.exec(
-        function(timeout) {
-            if (timeout < (frequency + 10000)) {
-                PhoneGap.exec(null, null, "Compass", "setTimeout", [frequency + 10000]);
-            }
-        },
-        function(e) { }, "Compass", "getTimeout", []);
-
-    // Start watch timer to get headings
-    var id = PhoneGap.createUUID();
-    navigator.compass.timers[id] = setInterval(
-        function() {
-            PhoneGap.exec(successCallback, errorCallback, "Compass", "getHeading", []);
-        }, (frequency ? frequency : 1));
-
-    return id;
-};
-
-
-/**
- * Clears the specified heading watch.
- *
- * @param {String} id       The ID of the watch returned from #watchHeading.
- */
-Compass.prototype.clearWatch = function(id) {
-
-    // Stop javascript timer & remove from timer list
-    if (id && navigator.compass.timers[id]) {
-        clearInterval(navigator.compass.timers[id]);
-        delete navigator.compass.timers[id];
-    }
-};
-
-PhoneGap.addConstructor(function() {
-    if (typeof navigator.compass === "undefined") {
-        navigator.compass = new Compass();
-    }
-});
-}
-
-
-/*
- * PhoneGap is available under *either* the terms of the modified BSD license *or* the
- * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- *
- * Copyright (c) 2005-2010, Nitobi Software Inc.
- * Copyright (c) 2010-2011, IBM Corporation
- */
-
-if (!PhoneGap.hasResource("contact")) {
-PhoneGap.addResource("contact");
-
-/**
-* Contains information about a single contact.
-* @constructor
-* @param {DOMString} id unique identifier
-* @param {DOMString} displayName
-* @param {ContactName} name
-* @param {DOMString} nickname
-* @param {Array.<ContactField>} phoneNumbers array of phone numbers
-* @param {Array.<ContactField>} emails array of email addresses
-* @param {Array.<ContactAddress>} addresses array of addresses
-* @param {Array.<ContactField>} ims instant messaging user ids
-* @param {Array.<ContactOrganization>} organizations
-* @param {DOMString} birthday contact's birthday
-* @param {DOMString} note user notes about contact
-* @param {Array.<ContactField>} photos
-* @param {Array.<ContactField>} categories
-* @param {Array.<ContactField>} urls contact's web sites
-*/
-var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses,
-    ims, organizations, birthday, note, photos, categories, urls) {
-    this.id = id || null;
-    this.rawId = null;
-    this.displayName = displayName || null;
-    this.name = name || null; // ContactName
-    this.nickname = nickname || null;
-    this.phoneNumbers = phoneNumbers || null; // ContactField[]
-    this.emails = emails || null; // ContactField[]
-    this.addresses = addresses || null; // ContactAddress[]
-    this.ims = ims || null; // ContactField[]
-    this.organizations = organizations || null; // ContactOrganization[]
-    this.birthday = birthday || null;
-    this.note = note || null;
-    this.photos = photos || null; // ContactField[]
-    this.categories = categories || null; // ContactField[]
-    this.urls = urls || null; // ContactField[]
-};
-
-/**
- *  ContactError.
- *  An error code assigned by an implementation when an error has occurreds
- * @constructor
- */
-var ContactError = function() {
-    this.code=null;
-};
-
-/**
- * Error codes
- */
-ContactError.UNKNOWN_ERROR = 0;
-ContactError.INVALID_ARGUMENT_ERROR = 1;
-ContactError.TIMEOUT_ERROR = 2;
-ContactError.PENDING_OPERATION_ERROR = 3;
-ContactError.IO_ERROR = 4;
-ContactError.NOT_SUPPORTED_ERROR = 5;
-ContactError.PERMISSION_DENIED_ERROR = 20;
-
-/**
-* Removes contact from device storage.
-* @param successCB success callback
-* @param errorCB error callback
-*/
-Contact.prototype.remove = function(successCB, errorCB) {
-    if (this.id === null) {
-        var errorObj = new ContactError();
-        errorObj.code = ContactError.UNKNOWN_ERROR;
-        errorCB(errorObj);
-    }
-    else {
-        PhoneGap.exec(successCB, errorCB, "Contacts", "remove", [this.id]);
-    }
-};
-
-/**
-* Creates a deep copy of this Contact.
-* With the contact ID set to null.
-* @return copy of this Contact
-*/
-Contact.prototype.clone = function() {
-    var clonedContact = PhoneGap.clone(this);
-    var i;
-    clonedContact.id = null;
-    clonedContact.rawId = null;
-    // Loop through and clear out any id's in phones, emails, etc.
-    if (clonedContact.phoneNumbers) {
-        for (i = 0; i < clonedContact.phoneNumbers.length; i++) {
-            clonedContact.phoneNumbers[i].id = null;
-        }
-    }
-    if (clonedContact.emails) {
-        for (i = 0; i < clonedContact.emails.length; i++) {
-            clonedContact.emails[i].id = null;
-        }
-    }
-    if (clonedContact.addresses) {
-        for (i = 0; i < clonedContact.addresses.length; i++) {
-            clonedContact.addresses[i].id = null;
-        }
-    }
-    if (clonedContact.ims) {
-        for (i = 0; i < clonedContact.ims.length; i++) {
-            clonedContact.ims[i].id = null;
-        }
-    }
-    if (clonedContact.organizations) {
-        for (i = 0; i < clonedContact.organizations.length; i++) {
-            clonedContact.organizations[i].id = null;
-        }
-    }
-    if (clonedContact.tags) {
-        for (i = 0; i < clonedContact.tags.length; i++) {
-            clonedContact.tags[i].id = null;
-        }
-    }
-    if (clonedContact.photos) {
-        for (i = 0; i < clonedContact.photos.length; i++) {
-            clonedContact.photos[i].id = null;
-        }
-    }
-    if (clonedContact.urls) {
-        for (i = 0; i < clonedContact.urls.length; i++) {
-            clonedContact.urls[i].id = null;
-        }
-    }
-    return clonedContact;
-};
-
-/**
-* Persists contact to device storage.
-* @param successCB success callback
-* @param errorCB error callback
-*/
-Contact.prototype.save = function(successCB, errorCB) {
-    PhoneGap.exec(successCB, errorCB, "Contacts", "save", [this]);
-};
-
-/**
-* Contact name.
-* @constructor
-* @param formatted
-* @param familyName
-* @param givenName
-* @param middle
-* @param prefix
-* @param suffix
-*/
-var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) {
-    this.formatted = formatted || null;
-    this.familyName = familyName || null;
-    this.givenName = givenName || null;
-    this.middleName = middle || null;
-    this.honorificPrefix = prefix || null;
-    this.honorificSuffix = suffix || null;
-};
-
-/**
-* Generic contact field.
-* @constructor
-* @param {DOMString} id unique identifier, should only be set by native code
-* @param type
-* @param value
-* @param pref
-*/
-var ContactField = function(type, value, pref) {
-       this.id = null;
-    this.type = type || null;
-    this.value = value || null;
-    this.pref = pref || null;
-};
-
-/**
-* Contact address.
-* @constructor
-* @param {DOMString} id unique identifier, should only be set by native code
-* @param formatted
-* @param streetAddress
-* @param locality
-* @param region
-* @param postalCode
-* @param country
-*/
-var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) {
-       this.id = null;
-    this.pref = pref || null;
-    this.type = type || null;
-    this.formatted = formatted || null;
-    this.streetAddress = streetAddress || null;
-    this.locality = locality || null;
-    this.region = region || null;
-    this.postalCode = postalCode || null;
-    this.country = country || null;
-};
-
-/**
-* Contact organization.
-* @constructor
-* @param {DOMString} id unique identifier, should only be set by native code
-* @param name
-* @param dept
-* @param title
-* @param startDate
-* @param endDate
-* @param location
-* @param desc
-*/
-var ContactOrganization = function(pref, type, name, dept, title) {
-       this.id = null;
-    this.pref = pref || null;
-    this.type = type || null;
-    this.name = name || null;
-    this.department = dept || null;
-    this.title = title || null;
-};
-
-/**
-* Represents a group of Contacts.
-* @constructor
-*/
-var Contacts = function() {
-    this.inProgress = false;
-    this.records = [];
-};
-/**
-* Returns an array of Contacts matching the search criteria.
-* @param fields that should be searched
-* @param successCB success callback
-* @param errorCB error callback
-* @param {ContactFindOptions} options that can be applied to contact searching
-* @return array of Contacts matching search criteria
-*/
-Contacts.prototype.find = function(fields, successCB, errorCB, options) {
-    if (successCB === null) {
-        throw new TypeError("You must specify a success callback for the find command.");
-    }
-    if (fields === null || fields === "undefined" || fields.length === "undefined" || fields.length <= 0) {
-        if (typeof errorCB === "function") {
-            errorCB({"code": ContactError.INVALID_ARGUMENT_ERROR});
-        }
-    } else {
-        PhoneGap.exec(successCB, errorCB, "Contacts", "search", [fields, options]);        
-    }
-};
-
-/**
-* This function creates a new contact, but it does not persist the contact
-* to device storage. To persist the contact to device storage, invoke
-* contact.save().
-* @param properties an object who's properties will be examined to create a new Contact
-* @returns new Contact object
-*/
-Contacts.prototype.create = function(properties) {
-    var i;
-       var contact = new Contact();
-    for (i in properties) {
-        if (contact[i] !== 'undefined') {
-            contact[i] = properties[i];
-        }
-    }
-    return contact;
-};
-
-/**
-* This function returns and array of contacts.  It is required as we need to convert raw
-* JSON objects into concrete Contact objects.  Currently this method is called after
-* navigator.contacts.find but before the find methods success call back.
-*
-* @param jsonArray an array of JSON Objects that need to be converted to Contact objects.
-* @returns an array of Contact objects
-*/
-Contacts.prototype.cast = function(pluginResult) {
-       var contacts = [];
-       var i;
-       for (i=0; i<pluginResult.message.length; i++) {
-               contacts.push(navigator.contacts.create(pluginResult.message[i]));
-       }
-       pluginResult.message = contacts;
-       return pluginResult;
-};
-
-/**
- * ContactFindOptions.
- * @constructor
- * @param filter used to match contacts against
- * @param multiple boolean used to determine if more than one contact should be returned
- */
-var ContactFindOptions = function(filter, multiple) {
-    this.filter = filter || '';
-    this.multiple = multiple || false;
-};
-
-/**
- * Add the contact interface into the browser.
- */
-PhoneGap.addConstructor(function() {
-    if(typeof navigator.contacts === "undefined") {
-        navigator.contacts = new Contacts();
-    }
-});
-}
-
-
-/*
- * PhoneGap is available under *either* the terms of the modified BSD license *or* the
- * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- *
- * Copyright (c) 2005-2010, Nitobi Software Inc.
- * Copyright (c) 2010-2011, IBM Corporation
- */
-
-// TODO: Needs to be commented
-
-if (!PhoneGap.hasResource("crypto")) {
-PhoneGap.addResource("crypto");
-
-/**
-* @constructor
-*/
-var Crypto = function() {
-};
-
-Crypto.prototype.encrypt = function(seed, string, callback) {
-    this.encryptWin = callback;
-    PhoneGap.exec(null, null, "Crypto", "encrypt", [seed, string]);
-};
-
-Crypto.prototype.decrypt = function(seed, string, callback) {
-    this.decryptWin = callback;
-    PhoneGap.exec(null, null, "Crypto", "decrypt", [seed, string]);
-};
-
-Crypto.prototype.gotCryptedString = function(string) {
-    this.encryptWin(string);
-};
-
-Crypto.prototype.getPlainString = function(string) {
-    this.decryptWin(string);
-};
-
-PhoneGap.addConstructor(function() {
-    if (typeof navigator.Crypto === "undefined") {
-        navigator.Crypto = new Crypto();
-    }
-});
-}
-
-
-/*
- * PhoneGap is available under *either* the terms of the modified BSD license *or* the
- * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- *
- * Copyright (c) 2005-2010, Nitobi Software Inc.
- * Copyright (c) 2010-2011, IBM Corporation
- */
-
-if (!PhoneGap.hasResource("device")) {
-PhoneGap.addResource("device");
-
-/**
- * This represents the mobile device, and provides properties for inspecting the model, version, UUID of the
- * phone, etc.
- * @constructor
- */
-var Device = function() {
-    this.available = PhoneGap.available;
-    this.platform = null;
-    this.version = null;
-    this.name = null;
-    this.uuid = null;
-    this.phonegap = null;
-
-    var me = this;
-    this.getInfo(
-        function(info) {
-            me.available = true;
-            me.platform = info.platform;
-            me.version = info.version;
-            me.name = info.name;
-            me.uuid = info.uuid;
-            me.phonegap = info.phonegap;
-            PhoneGap.onPhoneGapInfoReady.fire();
-        },
-        function(e) {
-            me.available = false;
-            console.log("Error initializing PhoneGap: " + e);
-            alert("Error initializing PhoneGap: "+e);
-        });
-};
-
-/**
- * Get device info
- *
- * @param {Function} successCallback The function to call when the heading data is available
- * @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL)
- */
-Device.prototype.getInfo = function(successCallback, errorCallback) {
-
-    // successCallback required
-    if (typeof successCallback !== "function") {
-        console.log("Device Error: successCallback is not a function");
-        return;
-    }
-
-    // errorCallback optional
-    if (errorCallback && (typeof errorCallback !== "function")) {
-        console.log("Device Error: errorCallback is not a function");
-        return;
-    }
-
-    // Get info
-    PhoneGap.exec(successCallback, errorCallback, "Device", "getDeviceInfo", []);
-};
-
-/*
- * DEPRECATED
- * This is only for Android.
- *
- * You must explicitly override the back button.
- */
-Device.prototype.overrideBackButton = function() {
-       console.log("Device.overrideBackButton() is deprecated.  Use App.overrideBackbutton(true).");
-       navigator.app.overrideBackbutton(true);
-};
-
-/*
- * DEPRECATED
- * This is only for Android.
- *
- * This resets the back button to the default behaviour
- */
-Device.prototype.resetBackButton = function() {
-       console.log("Device.resetBackButton() is deprecated.  Use App.overrideBackbutton(false).");
-       navigator.app.overrideBackbutton(false);
-};
-
-/*
- * DEPRECATED
- * This is only for Android.
- *
- * This terminates the activity!
- */
-Device.prototype.exitApp = function() {
-       console.log("Device.exitApp() is deprecated.  Use App.exitApp().");
-       navigator.app.exitApp();
-};
-
-PhoneGap.addConstructor(function() {
-    if (typeof navigator.device === "undefined") {
-        navigator.device = window.device = new Device();
-    }
-});
-}
-
-
-/*
- * PhoneGap is available under *either* the terms of the modified BSD license *or* the
- * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- *
- * Copyright (c) 2005-2010, Nitobi Software Inc.
- * Copyright (c) 2010-2011, IBM Corporation
- */
-
-if (!PhoneGap.hasResource("file")) {
-PhoneGap.addResource("file");
-
-/**
- * This class provides some useful information about a file.
- * This is the fields returned when navigator.fileMgr.getFileProperties()
- * is called.
- * @constructor
- */
-var FileProperties = function(filePath) {
-    this.filePath = filePath;
-    this.size = 0;
-    this.lastModifiedDate = null;
-};
-
-/**
- * Represents a single file.
- *
- * @constructor
- * @param name {DOMString} name of the file, without path information
- * @param fullPath {DOMString} the full path of the file, including the name
- * @param type {DOMString} mime type
- * @param lastModifiedDate {Date} last modified date
- * @param size {Number} size of the file in bytes
- */
-var File = function(name, fullPath, type, lastModifiedDate, size) {
-       this.name = name || null;
-    this.fullPath = fullPath || null;
-       this.type = type || null;
-    this.lastModifiedDate = lastModifiedDate || null;
-    this.size = size || 0;
-};
-
-/** @constructor */
-var FileError = function() {
-   this.code = null;
-};
-
-// File error codes
-// Found in DOMException
-FileError.NOT_FOUND_ERR = 1;
-FileError.SECURITY_ERR = 2;
-FileError.ABORT_ERR = 3;
-
-// Added by this specification
-FileError.NOT_READABLE_ERR = 4;
-FileError.ENCODING_ERR = 5;
-FileError.NO_MODIFICATION_ALLOWED_ERR = 6;
-FileError.INVALID_STATE_ERR = 7;
-FileError.SYNTAX_ERR = 8;
-FileError.INVALID_MODIFICATION_ERR = 9;
-FileError.QUOTA_EXCEEDED_ERR = 10;
-FileError.TYPE_MISMATCH_ERR = 11;
-FileError.PATH_EXISTS_ERR = 12;
-
-//-----------------------------------------------------------------------------
-// File manager
-//-----------------------------------------------------------------------------
-
-/** @constructor */
-var FileMgr = function() {
-};
-
-FileMgr.prototype.getFileProperties = function(filePath) {
-    return PhoneGap.exec(null, null, "File", "getFileProperties", [filePath]);
-};
-
-FileMgr.prototype.getFileBasePaths = function() {
-};
-
-FileMgr.prototype.testSaveLocationExists = function(successCallback, errorCallback) {
-    return PhoneGap.exec(successCallback, errorCallback, "File", "testSaveLocationExists", []);
-};
-
-FileMgr.prototype.testFileExists = function(fileName, successCallback, errorCallback) {
-    return PhoneGap.exec(successCallback, errorCallback, "File", "testFileExists", [fileName]);
-};
-
-FileMgr.prototype.testDirectoryExists = function(dirName, successCallback, errorCallback) {
-    return PhoneGap.exec(successCallback, errorCallback, "File", "testDirectoryExists", [dirName]);
-};
-
-FileMgr.prototype.getFreeDiskSpace = function(successCallback, errorCallback) {
-    return PhoneGap.exec(successCallback, errorCallback, "File", "getFreeDiskSpace", []);
-};
-
-FileMgr.prototype.write = function(fileName, data, position, successCallback, errorCallback) {
-    PhoneGap.exec(successCallback, errorCallback, "File", "write", [fileName, data, position]);
-};
-
-FileMgr.prototype.truncate = function(fileName, size, successCallback, errorCallback) {
-    PhoneGap.exec(successCallback, errorCallback, "File", "truncate", [fileName, size]);
-};
-
-FileMgr.prototype.readAsText = function(fileName, encoding, successCallback, errorCallback) {
-    PhoneGap.exec(successCallback, errorCallback, "File", "readAsText", [fileName, encoding]);
-};
-
-FileMgr.prototype.readAsDataURL = function(fileName, successCallback, errorCallback) {
-    PhoneGap.exec(successCallback, errorCallback, "File", "readAsDataURL", [fileName]);
-};
-
-PhoneGap.addConstructor(function() {
-    if (typeof navigator.fileMgr === "undefined") {
-        navigator.fileMgr = new FileMgr();
-    }
-});
-
-//-----------------------------------------------------------------------------
-// File Reader
-//-----------------------------------------------------------------------------
-// TODO: All other FileMgr function operate on the SD card as root.  However,
-//       for FileReader & FileWriter the root is not SD card.  Should this be changed?
-
-/**
- * This class reads the mobile device file system.
- *
- * For Android:
- *      The root directory is the root of the file system.
- *      To read from the SD card, the file name is "sdcard/my_file.txt"
- * @constructor
- */
-var FileReader = function() {
-    this.fileName = "";
-
-    this.readyState = 0;
-
-    // File data
-    this.result = null;
-
-    // Error
-    this.error = null;
-
-    // Event handlers
-    this.onloadstart = null;    // When the read starts.
-    this.onprogress = null;     // While reading (and decoding) file or fileBlob data, and reporting partial file data (progess.loaded/progress.total)
-    this.onload = null;         // When the read has successfully completed.
-    this.onerror = null;        // When the read has failed (see errors).
-    this.onloadend = null;      // When the request has completed (either in success or failure).
-    this.onabort = null;        // When the read has been aborted. For instance, by invoking the abort() method.
-};
-
-// States
-FileReader.EMPTY = 0;
-FileReader.LOADING = 1;
-FileReader.DONE = 2;
-
-/**
- * Abort reading file.
- */
-FileReader.prototype.abort = function() {
-    var evt;
-    this.readyState = FileReader.DONE;
-    this.result = null;
-
-    // set error
-    var error = new FileError();
-    error.code = error.ABORT_ERR;
-    this.error = error;
-
-    // If error callback
-    if (typeof this.onerror === "function") {
-        this.onerror({"type":"error", "target":this});
-    }
-    // If abort callback
-    if (typeof this.onabort === "function") {
-        this.onabort({"type":"abort", "target":this});
-    }
-    // If load end callback
-    if (typeof this.onloadend === "function") {
-        this.onloadend({"type":"loadend", "target":this});
-    }
-};
-
-/**
- * Read text file.
- *
- * @param file          {File} File object containing file properties
- * @param encoding      [Optional] (see http://www.iana.org/assignments/character-sets)
- */
-FileReader.prototype.readAsText = function(file, encoding) {
-    this.fileName = "";
-       if (typeof file.fullPath === "undefined") {
-               this.fileName = file;
-       } else {
-               this.fileName = file.fullPath;
-       }
-
-    // LOADING state
-    this.readyState = FileReader.LOADING;
-
-    // If loadstart callback
-    if (typeof this.onloadstart === "function") {
-        this.onloadstart({"type":"loadstart", "target":this});
-    }
-
-    // Default encoding is UTF-8
-    var enc = encoding ? encoding : "UTF-8";
-
-    var me = this;
-
-    // Read file
-    navigator.fileMgr.readAsText(this.fileName, enc,
-
-        // Success callback
-        function(r) {
-            var evt;
-
-            // If DONE (cancelled), then don't do anything
-            if (me.readyState === FileReader.DONE) {
-                return;
-            }
-
-            // Save result
-            me.result = r;
-
-            // If onload callback
-            if (typeof me.onload === "function") {
-                me.onload({"type":"load", "target":me});
-            }
-
-            // DONE state
-            me.readyState = FileReader.DONE;
-
-            // If onloadend callback
-            if (typeof me.onloadend === "function") {
-                me.onloadend({"type":"loadend", "target":me});
-            }
-        },
-
-        // Error callback
-        function(e) {
-            var evt;
-            // If DONE (cancelled), then don't do anything
-            if (me.readyState === FileReader.DONE) {
-                return;
-            }
-
-            // Save error
-                   me.error = e;
-
-            // If onerror callback
-            if (typeof me.onerror === "function") {
-                me.onerror({"type":"error", "target":me});
-            }
-
-            // DONE state
-            me.readyState = FileReader.DONE;
-
-            // If onloadend callback
-            if (typeof me.onloadend === "function") {
-                me.onloadend({"type":"loadend", "target":me});
-            }
-        }
-        );
-};
-
-
-/**
- * Read file and return data as a base64 encoded data url.
- * A data url is of the form:
- *      data:[<mediatype>][;base64],<data>
- *
- * @param file          {File} File object containing file properties
- */
-FileReader.prototype.readAsDataURL = function(file) {
-       this.fileName = "";
-    if (typeof file.fullPath === "undefined") {
-        this.fileName = file;
-    } else {
-        this.fileName = file.fullPath;
-    }
-
-    // LOADING state
-    this.readyState = FileReader.LOADING;
-
-    // If loadstart callback
-    if (typeof this.onloadstart === "function") {
-        this.onloadstart({"type":"loadstart", "target":this});
-    }
-
-    var me = this;
-
-    // Read file
-    navigator.fileMgr.readAsDataURL(this.fileName,
-
-        // Success callback
-        function(r) {
-            var evt;
-
-            // If DONE (cancelled), then don't do anything
-            if (me.readyState === FileReader.DONE) {
-                return;
-            }
-
-            // Save result
-            me.result = r;
-
-            // If onload callback
-            if (typeof me.onload === "function") {
-                me.onload({"type":"load", "target":me});
-            }
-
-            // DONE state
-            me.readyState = FileReader.DONE;
-
-            // If onloadend callback
-            if (typeof me.onloadend === "function") {
-                me.onloadend({"type":"loadend", "target":me});
-            }
-        },
-
-        // Error callback
-        function(e) {
-            var evt;
-            // If DONE (cancelled), then don't do anything
-            if (me.readyState === FileReader.DONE) {
-                return;
-            }
-
-            // Save error
-            me.error = e;
-
-            // If onerror callback
-            if (typeof me.onerror === "function") {
-                me.onerror({"type":"error", "target":me});
-            }
-
-            // DONE state
-            me.readyState = FileReader.DONE;
-
-            // If onloadend callback
-            if (typeof me.onloadend === "function") {
-                me.onloadend({"type":"loadend", "target":me});
-            }
-        }
-        );
-};
-
-/**
- * Read file and return data as a binary data.
- *
- * @param file          {File} File object containing file properties
- */
-FileReader.prototype.readAsBinaryString = function(file) {
-    // TODO - Can't return binary data to browser.
-    this.fileName = file;
-};
-
-/**
- * Read file and return data as a binary data.
- *
- * @param file          {File} File object containing file properties
- */
-FileReader.prototype.readAsArrayBuffer = function(file) {
-    // TODO - Can't return binary data to browser.
-    this.fileName = file;
-};
-
-//-----------------------------------------------------------------------------
-// File Writer
-//-----------------------------------------------------------------------------
-
-/**
- * This class writes to the mobile device file system.
- *
- * For Android:
- *      The root directory is the root of the file system.
- *      To write to the SD card, the file name is "sdcard/my_file.txt"
- *
- * @constructor
- * @param file {File} File object containing file properties
- * @param append if true write to the end of the file, otherwise overwrite the file
- */
-var FileWriter = function(file) {
-    this.fileName = "";
-    this.length = 0;
-       if (file) {
-           this.fileName = file.fullPath || file;
-           this.length = file.size || 0;
-       }
-    // default is to write at the beginning of the file
-    this.position = 0;
-
-    this.readyState = 0; // EMPTY
-
-    this.result = null;
-
-    // Error
-    this.error = null;
-
-    // Event handlers
-    this.onwritestart = null;  // When writing starts
-    this.onprogress = null;            // While writing the file, and reporting partial file data
-    this.onwrite = null;               // When the write has successfully completed.
-    this.onwriteend = null;            // When the request has completed (either in success or failure).
-    this.onabort = null;               // When the write has been aborted. For instance, by invoking the abort() method.
-    this.onerror = null;               // When the write has failed (see errors).
-};
-
-// States
-FileWriter.INIT = 0;
-FileWriter.WRITING = 1;
-FileWriter.DONE = 2;
-
-/**
- * Abort writing file.
- */
-FileWriter.prototype.abort = function() {
-    // check for invalid state
-       if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) {
-               throw FileError.INVALID_STATE_ERR;
-       }
-
-    // set error
-    var error = new FileError(), evt;
-    error.code = error.ABORT_ERR;
-    this.error = error;
-
-    // If error callback
-    if (typeof this.onerror === "function") {
-        this.onerror({"type":"error", "target":this});
-    }
-    // If abort callback
-    if (typeof this.onabort === "function") {
-        this.onabort({"type":"abort", "target":this});
-    }
-
-    this.readyState = FileWriter.DONE;
-
-    // If write end callback
-    if (typeof this.onwriteend == "function") {
-        this.onwriteend({"type":"writeend", "target":this});
-    }
-};
-
-/**
- * Writes data to the file
- *
- * @param text to be written
- */
-FileWriter.prototype.write = function(text) {
-       // Throw an exception if we are already writing a file
-       if (this.readyState === FileWriter.WRITING) {
-               throw FileError.INVALID_STATE_ERR;
-       }
-
-    // WRITING state
-    this.readyState = FileWriter.WRITING;
-
-    var me = this;
-
-    // If onwritestart callback
-    if (typeof me.onwritestart === "function") {
-        me.onwritestart({"type":"writestart", "target":me});
-    }
-
-    // Write file
-    navigator.fileMgr.write(this.fileName, text, this.position,
-
-        // Success callback
-        function(r) {
-            var evt;
-            // If DONE (cancelled), then don't do anything
-            if (me.readyState === FileWriter.DONE) {
-                return;
-            }
-
-            // position always increases by bytes written because file would be extended
-            me.position += r;
-            // The length of the file is now where we are done writing.
-            me.length = me.position;
-
-            // If onwrite callback
-            if (typeof me.onwrite === "function") {
-                me.onwrite({"type":"write", "target":me});
-            }
-
-            // DONE state
-            me.readyState = FileWriter.DONE;
-
-            // If onwriteend callback
-            if (typeof me.onwriteend === "function") {
-                me.onwriteend({"type":"writeend", "target":me});
-            }
-        },
-
-        // Error callback
-        function(e) {
-            var evt;
-
-            // If DONE (cancelled), then don't do anything
-            if (me.readyState === FileWriter.DONE) {
-                return;
-            }
-
-            // Save error
-            me.error = e;
-
-            // If onerror callback
-            if (typeof me.onerror === "function") {
-                me.onerror({"type":"error", "target":me});
-            }
-
-            // DONE state
-            me.readyState = FileWriter.DONE;
-
-            // If onwriteend callback
-            if (typeof me.onwriteend === "function") {
-                me.onwriteend({"type":"writeend", "target":me});
-            }
-        }
-        );
-
-};
-
-/**
- * Moves the file pointer to the location specified.
- *
- * If the offset is a negative number the position of the file
- * pointer is rewound.  If the offset is greater than the file
- * size the position is set to the end of the file.
- *
- * @param offset is the location to move the file pointer to.
- */
-FileWriter.prototype.seek = function(offset) {
-    // Throw an exception if we are already writing a file
-    if (this.readyState === FileWriter.WRITING) {
-        throw FileError.INVALID_STATE_ERR;
-    }
-
-    if (!offset) {
-        return;
-    }
-
-    // See back from end of file.
-    if (offset < 0) {
-               this.position = Math.max(offset + this.length, 0);
-       }
-    // Offset is bigger then file size so set position
-    // to the end of the file.
-       else if (offset > this.length) {
-               this.position = this.length;
-       }
-    // Offset is between 0 and file size so set the position
-    // to start writing.
-       else {
-               this.position = offset;
-       }
-};
-
-/**
- * Truncates the file to the size specified.
- *
- * @param size to chop the file at.
- */
-FileWriter.prototype.truncate = function(size) {
-       // Throw an exception if we are already writing a file
-       if (this.readyState === FileWriter.WRITING) {
-               throw FileError.INVALID_STATE_ERR;
-       }
-
-    // WRITING state
-    this.readyState = FileWriter.WRITING;
-
-    var me = this;
-
-    // If onwritestart callback
-    if (typeof me.onwritestart === "function") {
-        me.onwritestart({"type":"writestart", "target":this});
-    }
-
-    // Write file
-    navigator.fileMgr.truncate(this.fileName, size,
-
-        // Success callback
-        function(r) {
-            var evt;
-            // If DONE (cancelled), then don't do anything
-            if (me.readyState === FileWriter.DONE) {
-                return;
-            }
-
-            // Update the length of the file
-            me.length = r;
-            me.position = Math.min(me.position, r);
-
-            // If onwrite callback
-            if (typeof me.onwrite === "function") {
-                me.onwrite({"type":"write", "target":me});
-            }
-
-            // DONE state
-            me.readyState = FileWriter.DONE;
-
-            // If onwriteend callback
-            if (typeof me.onwriteend === "function") {
-                me.onwriteend({"type":"writeend", "target":me});
-            }
-        },
-
-        // Error callback
-        function(e) {
-            var evt;
-            // If DONE (cancelled), then don't do anything
-            if (me.readyState === FileWriter.DONE) {
-                return;
-            }
-
-            // Save error
-            me.error = e;
-
-            // If onerror callback
-            if (typeof me.onerror === "function") {
-                me.onerror({"type":"error", "target":me});
-            }
-
-            // DONE state
-            me.readyState = FileWriter.DONE;
-
-            // If onwriteend callback
-            if (typeof me.onwriteend === "function") {
-                me.onwriteend({"type":"writeend", "target":me});
-            }
-        }
-    );
-};
-
-/**
- * Information about the state of the file or directory
- *
- * @constructor
- * {Date} modificationTime (readonly)
- */
-var Metadata = function() {
-    this.modificationTime=null;
-};
-
-/**
- * Supplies arguments to methods that lookup or create files and directories
- *
- * @constructor
- * @param {boolean} create file or directory if it doesn't exist
- * @param {boolean} exclusive if true the command will fail if the file or directory exists
- */
-var Flags = function(create, exclusive) {
-    this.create = create || false;
-    this.exclusive = exclusive || false;
-};
-
-/**
- * An interface representing a file system
- *
- * @constructor
- * {DOMString} name the unique name of the file system (readonly)
- * {DirectoryEntry} root directory of the file system (readonly)
- */
-var FileSystem = function() {
-    this.name = null;
-    this.root = null;
-};
-
-/**
- * An interface that lists the files and directories in a directory.
- * @constructor
- */
-var DirectoryReader = function(fullPath){
-    this.fullPath = fullPath || null;
-};
-
-/**
- * Returns a list of entries from a directory.
- *
- * @param {Function} successCallback is called with a list of entries
- * @param {Function} errorCallback is called with a FileError
- */
-DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) {
-    PhoneGap.exec(successCallback, errorCallback, "File", "readEntries", [this.fullPath]);
-};
-
-/**
- * An interface representing a directory on the file system.
- *
- * @constructor
- * {boolean} isFile always false (readonly)
- * {boolean} isDirectory always true (readonly)
- * {DOMString} name of the directory, excluding the path leading to it (readonly)
- * {DOMString} fullPath the absolute full path to the directory (readonly)
- * {FileSystem} filesystem on which the directory resides (readonly)
- */
-var DirectoryEntry = function() {
-    this.isFile = false;
-    this.isDirectory = true;
-    this.name = null;
-    this.fullPath = null;
-    this.filesystem = null;
-};
-
-/**
- * Copies a directory to a new location
- *
- * @param {DirectoryEntry} parent the directory to which to copy the entry
- * @param {DOMString} newName the new name of the entry, defaults to the current name
- * @param {Function} successCallback is called with the new entry
- * @param {Function} errorCallback is called with a FileError
- */
-DirectoryEntry.prototype.copyTo = function(parent, newName, successCallback, errorCallback) {
-    PhoneGap.exec(successCallback, errorCallback, "File", "copyTo", [this.fullPath, parent, newName]);
-};
-
-/**
- * Looks up the metadata of the entry
- *
- * @param {Function} successCallback is called with a Metadata object
- * @param {Function} errorCallback is called with a FileError
- */
-DirectoryEntry.prototype.getMetadata = function(successCallback, errorCallback) {
-    PhoneGap.exec(successCallback, errorCallback, "File", "getMetadata", [this.fullPath]);
-};
-
-/**
- * Gets the parent of the entry
- *
- * @param {Function} successCallback is called with a parent entry
- * @param {Function} errorCallback is called with a FileError
- */
-DirectoryEntry.prototype.getParent = function(successCallback, errorCallback) {
-    PhoneGap.exec(successCallback, errorCallback, "File", "getParent", [this.fullPath]);
-};
-
-/**
- * Moves a directory to a new location
- *
- * @param {DirectoryEntry} parent the directory to which to move the entry
- * @param {DOMString} newName the new name of the entry, defaults to the current name
- * @param {Function} successCallback is called with the new entry
- * @param {Function} errorCallback is called with a FileError
- */
-DirectoryEntry.prototype.moveTo = function(parent, newName, successCallback, errorCallback) {
-    PhoneGap.exec(successCallback, errorCallback, "File", "moveTo", [this.fullPath, parent, newName]);
-};
-
-/**
- * Removes the entry
- *
- * @param {Function} successCallback is called with no parameters
- * @param {Function} errorCallback is called with a FileError
- */
-DirectoryEntry.prototype.remove = function(successCallback, errorCallback) {
-    PhoneGap.exec(successCallback, errorCallback, "File", "remove", [this.fullPath]);
-};
-
-/**
- * Returns a URI that can be used to identify this entry.
- *
- * @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI.
- * @return uri
- */
-DirectoryEntry.prototype.toURI = function(mimeType) {
-    return "file://" + this.fullPath;
-};
-
-/**
- * Creates a new DirectoryReader to read entries from this directory
- */
-DirectoryEntry.prototype.createReader = function(successCallback, errorCallback) {
-    return new DirectoryReader(this.fullPath);
-};
-
-/**
- * Creates or looks up a directory
- *
- * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory
- * @param {Flags} options to create or excluively create the directory
- * @param {Function} successCallback is called with the new entry
- * @param {Function} errorCallback is called with a FileError
- */
-DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) {
-    PhoneGap.exec(successCallback, errorCallback, "File", "getDirectory", [this.fullPath, path, options]);
-};
-
-/**
- * Creates or looks up a file
- *
- * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file
- * @param {Flags} options to create or excluively create the file
- * @param {Function} successCallback is called with the new entry
- * @param {Function} errorCallback is called with a FileError
- */
-DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) {
-    PhoneGap.exec(successCallback, errorCallback, "File", "getFile", [this.fullPath, path, options]);
-};
-
-/**
- * Deletes a directory and all of it's contents
- *
- * @param {Function} successCallback is called with no parameters
- * @param {Function} errorCallback is called with a FileError
- */
-DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) {
-    PhoneGap.exec(successCallback, errorCallback, "File", "removeRecursively", [this.fullPath]);
-};
-
-/**
- * An interface representing a directory on the file system.
- *
- * @constructor
- * {boolean} isFile always true (readonly)
- * {boolean} isDirectory always false (readonly)
- * {DOMString} name of the file, excluding the path leading to it (readonly)
- * {DOMString} fullPath the absolute full path to the file (readonly)
- * {FileSystem} filesystem on which the directory resides (readonly)
- */
-var FileEntry = function() {
-    this.isFile = true;
-    this.isDirectory = false;
-    this.name = null;
-    this.fullPath = null;
-    this.filesystem = null;
-};
-
-/**
- * Copies a file to a new location
- *
- * @param {DirectoryEntry} parent the directory to which to copy the entry
- * @param {DOMString} newName the new name of the entry, defaults to the current name
- * @param {Function} successCallback is called with the new entry
- * @param {Function} errorCallback is called with a FileError
- */
-FileEntry.prototype.copyTo = function(parent, newName, successCallback, errorCallback) {
-    PhoneGap.exec(successCallback, errorCallback, "File", "copyTo", [this.fullPath, parent, newName]);
-};
-
-/**
- * Looks up the metadata of the entry
- *
- * @param {Function} successCallback is called with a Metadata object
- * @param {Function} errorCallback is called with a FileError
- */
-FileEntry.prototype.getMetadata = function(successCallback, errorCallback) {
-    PhoneGap.exec(successCallback, errorCallback, "File", "getMetadata", [this.fullPath]);
-};
-
-/**
- * Gets the parent of the entry
- *
- * @param {Function} successCallback is called with a parent entry
- * @param {Function} errorCallback is called with a FileError
- */
-FileEntry.prototype.getParent = function(successCallback, errorCallback) {
-    PhoneGap.exec(successCallback, errorCallback, "File", "getParent", [this.fullPath]);
-};
-
-/**
- * Moves a directory to a new location
- *
- * @param {DirectoryEntry} parent the directory to which to move the entry
- * @param {DOMString} newName the new name of the entry, defaults to the current name
- * @param {Function} successCallback is called with the new entry
- * @param {Function} errorCallback is called with a FileError
- */
-FileEntry.prototype.moveTo = function(parent, newName, successCallback, errorCallback) {
-    PhoneGap.exec(successCallback, errorCallback, "File", "moveTo", [this.fullPath, parent, newName]);
-};
-
-/**
- * Removes the entry
- *
- * @param {Function} successCallback is called with no parameters
- * @param {Function} errorCallback is called with a FileError
- */
-FileEntry.prototype.remove = function(successCallback, errorCallback) {
-    PhoneGap.exec(successCallback, errorCallback, "File", "remove", [this.fullPath]);
-};
-
-/**
- * Returns a URI that can be used to identify this entry.
- *
- * @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI.
- * @return uri
- */
-FileEntry.prototype.toURI = function(mimeType) {
-    return "file://" + this.fullPath;
-};
-
-/**
- * Creates a new FileWriter associated with the file that this FileEntry represents.
- *
- * @param {Function} successCallback is called with the new FileWriter
- * @param {Function} errorCallback is called with a FileError
- */
-FileEntry.prototype.createWriter = function(successCallback, errorCallback) {
-    this.file(function(filePointer) {
-        var writer = new FileWriter(filePointer);
-    
-        if (writer.fileName === null || writer.fileName === "") {
-            if (typeof errorCallback == "function") {
-                errorCallback({
-                    "code": FileError.INVALID_STATE_ERR
-                });
-            }
-        }
-    
-        if (typeof successCallback == "function") {
-            successCallback(writer);
-        }       
-    }, errorCallback);
-};
-
-/**
- * Returns a File that represents the current state of the file that this FileEntry represents.
- *
- * @param {Function} successCallback is called with the new File object
- * @param {Function} errorCallback is called with a FileError
- */
-FileEntry.prototype.file = function(successCallback, errorCallback) {
-    PhoneGap.exec(successCallback, errorCallback, "File", "getFileMetadata", [this.fullPath]);
-};
-
-/** @constructor */
-var LocalFileSystem = function() {
-};
-
-// File error codes
-LocalFileSystem.TEMPORARY = 0;
-LocalFileSystem.PERSISTENT = 1;
-LocalFileSystem.RESOURCE = 2;
-LocalFileSystem.APPLICATION = 3;
-
-/**
- * Requests a filesystem in which to store application data.
- *
- * @param {int} type of file system being requested
- * @param {Function} successCallback is called with the new FileSystem
- * @param {Function} errorCallback is called with a FileError
- */
-LocalFileSystem.prototype.requestFileSystem = function(type, size, successCallback, errorCallback) {
-    if (type < 0 || type > 3) {
-        if (typeof errorCallback == "function") {
-            errorCallback({
-                "code": FileError.SYNTAX_ERR
-            });
-        }
-    }
-    else {
-        PhoneGap.exec(successCallback, errorCallback, "File", "requestFileSystem", [type, size]);
-    }
-};
-
-/**
- *
- * @param {DOMString} uri referring to a local file in a filesystem
- * @param {Function} successCallback is called with the new entry
- * @param {Function} errorCallback is called with a FileError
- */
-LocalFileSystem.prototype.resolveLocalFileSystemURI = function(uri, successCallback, errorCallback) {
-    PhoneGap.exec(successCallback, errorCallback, "File", "resolveLocalFileSystemURI", [uri]);
-};
-
-/**
-* This function returns and array of contacts.  It is required as we need to convert raw
-* JSON objects into concrete Contact objects.  Currently this method is called after
-* navigator.service.contacts.find but before the find methods success call back.
-*
-* @param a JSON Objects that need to be converted to DirectoryEntry or FileEntry objects.
-* @returns an entry
-*/
-LocalFileSystem.prototype._castFS = function(pluginResult) {
-    var entry = null;
-    entry = new DirectoryEntry();
-    entry.isDirectory = pluginResult.message.root.isDirectory;
-    entry.isFile = pluginResult.message.root.isFile;
-    entry.name = pluginResult.message.root.name;
-    entry.fullPath = pluginResult.message.root.fullPath;
-    pluginResult.message.root = entry;
-    return pluginResult;
-};
-
-LocalFileSystem.prototype._castEntry = function(pluginResult) {
-    var entry = null;
-    if (pluginResult.message.isDirectory) {
-        console.log("This is a dir");
-        entry = new DirectoryEntry();
-    }
-    else if (pluginResult.message.isFile) {
-        console.log("This is a file");
-        entry = new FileEntry();
-    }
-    entry.isDirectory = pluginResult.message.isDirectory;
-    entry.isFile = pluginResult.message.isFile;
-    entry.name = pluginResult.message.name;
-    entry.fullPath = pluginResult.message.fullPath;
-    pluginResult.message = entry;
-    return pluginResult;
-};
-
-LocalFileSystem.prototype._castEntries = function(pluginResult) {
-    var entries = pluginResult.message;
-    var retVal = [];
-    for (var i=0; i<entries.length; i++) {
-        retVal.push(window.localFileSystem._createEntry(entries[i]));
-    }
-    pluginResult.message = retVal;
-    return pluginResult;
-};
-
-LocalFileSystem.prototype._createEntry = function(castMe) {
-    var entry = null;
-    if (castMe.isDirectory) {
-        console.log("This is a dir");
-        entry = new DirectoryEntry();
-    }
-    else if (castMe.isFile) {
-        console.log("This is a file");
-        entry = new FileEntry();
-    }
-    entry.isDirectory = castMe.isDirectory;
-    entry.isFile = castMe.isFile;
-    entry.name = castMe.name;
-    entry.fullPath = castMe.fullPath;
-    return entry;
-};
-
-LocalFileSystem.prototype._castDate = function(pluginResult) {
-    if (pluginResult.message.modificationTime) {
-        var modTime = new Date(pluginResult.message.modificationTime);
-        pluginResult.message.modificationTime = modTime;
-    }
-    else if (pluginResult.message.lastModifiedDate) {
-        var file = new File();
-        file.size = pluginResult.message.size;
-        file.type = pluginResult.message.type;
-        file.name = pluginResult.message.name;
-        file.fullPath = pluginResult.message.fullPath;
-        file.lastModifiedDate = new Date(pluginResult.message.lastModifiedDate);
-        pluginResult.message = file;
-    }
-    return pluginResult;
-};
-
-/**
- * Add the FileSystem interface into the browser.
- */
-PhoneGap.addConstructor(function() {
-       var pgLocalFileSystem = new LocalFileSystem();
-       // Needed for cast methods
-    if(typeof window.localFileSystem == "undefined") window.localFileSystem  = pgLocalFileSystem;
-    if(typeof window.requestFileSystem == "undefined") window.requestFileSystem  = pgLocalFileSystem.requestFileSystem;
-    if(typeof window.resolveLocalFileSystemURI == "undefined") window.resolveLocalFileSystemURI = pgLocalFileSystem.resolveLocalFileSystemURI;
-});
-}
-
-
-/*
- * PhoneGap is available under *either* the terms of the modified BSD license *or* the
- * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- *
- * Copyright (c) 2005-2010, Nitobi Software Inc.
- * Copyright (c) 2010-2011, IBM Corporation
- */
-
-if (!PhoneGap.hasResource("filetransfer")) {
-PhoneGap.addResource("filetransfer");
-
-/**
- * FileTransfer uploads a file to a remote server.
- * @constructor
- */
-var FileTransfer = function() {};
-
-/**
- * FileUploadResult
- * @constructor
- */
-var FileUploadResult = function() {
-    this.bytesSent = 0;
-    this.responseCode = null;
-    this.response = null;
-};
-
-/**
- * FileTransferError
- * @constructor
- */
-var FileTransferError = function() {
-    this.code = null;
-};
-
-FileTransferError.FILE_NOT_FOUND_ERR = 1;
-FileTransferError.INVALID_URL_ERR = 2;
-FileTransferError.CONNECTION_ERR = 3;
-
-/**
-* Given an absolute file path, uploads a file on the device to a remote server
-* using a multipart HTTP request.
-* @param filePath {String}           Full path of the file on the device
-* @param server {String}             URL of the server to receive the file
-* @param successCallback (Function}  Callback to be invoked when upload has completed
-* @param errorCallback {Function}    Callback to be invoked upon error
-* @param options {FileUploadOptions} Optional parameters such as file name and mimetype
-*/
-FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, debug) {
-
-    // check for options
-    var fileKey = null;
-    var fileName = null;
-    var mimeType = null;
-    var params = null;
-    if (options) {
-        fileKey = options.fileKey;
-        fileName = options.fileName;
-        mimeType = options.mimeType;
-        if (options.params) {
-            params = options.params;
-        }
-        else {
-            params = {};
-        }
-    }
-
-    PhoneGap.exec(successCallback, errorCallback, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, debug]);
-};
-
-/**
- * Options to customize the HTTP request used to upload files.
- * @constructor
- * @param fileKey {String}   Name of file request parameter.
- * @param fileName {String}  Filename to be used by the server. Defaults to image.jpg.
- * @param mimeType {String}  Mimetype of the uploaded file. Defaults to image/jpeg.
- * @param params {Object}    Object with key: value params to send to the server.
- */
-var FileUploadOptions = function(fileKey, fileName, mimeType, params) {
-    this.fileKey = fileKey || null;
-    this.fileName = fileName || null;
-    this.mimeType = mimeType || null;
-    this.params = params || null;
-};
-}
-
-
-/*
- * PhoneGap is available under *either* the terms of the modified BSD license *or* the
- * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- *
- * Copyright (c) 2005-2010, Nitobi Software Inc.
- * Copyright (c) 2010-2011, IBM Corporation
- */
-
-if (!PhoneGap.hasResource("geolocation")) {
-PhoneGap.addResource("geolocation");
-
-/**
- * This class provides access to device GPS data.
- * @constructor
- */
-var Geolocation = function() {
-
-    // The last known GPS position.
-    this.lastPosition = null;
-
-    // Geolocation listeners
-    this.listeners = {};
-};
-
-/**
- * Position error object
- *
- * @constructor
- * @param code
- * @param message
- */
-var PositionError = function(code, message) {
-    this.code = code;
-    this.message = message;
-};
-
-PositionError.PERMISSION_DENIED = 1;
-PositionError.POSITION_UNAVAILABLE = 2;
-PositionError.TIMEOUT = 3;
-
-/**
- * Asynchronously aquires the current position.
- *
- * @param {Function} successCallback    The function to call when the position data is available
- * @param {Function} errorCallback      The function to call when there is an error getting the heading position. (OPTIONAL)
- * @param {PositionOptions} options     The options for getting the position data. (OPTIONAL)
- */
-Geolocation.prototype.getCurrentPosition = function(successCallback, errorCallback, options) {
-    if (navigator._geo.listeners.global) {
-        console.log("Geolocation Error: Still waiting for previous getCurrentPosition() request.");
-        try {
-            errorCallback(new PositionError(PositionError.TIMEOUT, "Geolocation Error: Still waiting for previous getCurrentPosition() request."));
-        } catch (e) {
-        }
-        return;
-    }
-    var maximumAge = 10000;
-    var enableHighAccuracy = false;
-    var timeout = 10000;
-    if (typeof options !== "undefined") {
-        if (typeof options.maximumAge !== "undefined") {
-            maximumAge = options.maximumAge;
-        }
-        if (typeof options.enableHighAccuracy !== "undefined") {
-            enableHighAccuracy = options.enableHighAccuracy;
-        }
-        if (typeof options.timeout !== "undefined") {
-            timeout = options.timeout;
-        }
-    }
-    navigator._geo.listeners.global = {"success" : successCallback, "fail" : errorCallback };
-    PhoneGap.exec(null, null, "Geolocation", "getCurrentLocation", [enableHighAccuracy, timeout, maximumAge]);
-};
-
-/**
- * Asynchronously watches the geolocation for changes to geolocation.  When a change occurs,
- * the successCallback is called with the new location.
- *
- * @param {Function} successCallback    The function to call each time the location data is available
- * @param {Function} errorCallback      The function to call when there is an error getting the location data. (OPTIONAL)
- * @param {PositionOptions} options     The options for getting the location data such as frequency. (OPTIONAL)
- * @return String                       The watch id that must be passed to #clearWatch to stop watching.
- */
-Geolocation.prototype.watchPosition = function(successCallback, errorCallback, options) {
-    var maximumAge = 10000;
-    var enableHighAccuracy = false;
-    var timeout = 10000;
-    if (typeof options !== "undefined") {
-        if (typeof options.frequency  !== "undefined") {
-            maximumAge = options.frequency;
-        }
-        if (typeof options.maximumAge !== "undefined") {
-            maximumAge = options.maximumAge;
-        }
-        if (typeof options.enableHighAccuracy !== "undefined") {
-            enableHighAccuracy = options.enableHighAccuracy;
-        }
-        if (typeof options.timeout !== "undefined") {
-            timeout = options.timeout;
-        }
-    }
-    var id = PhoneGap.createUUID();
-    navigator._geo.listeners[id] = {"success" : successCallback, "fail" : errorCallback };
-    PhoneGap.exec(null, null, "Geolocation", "start", [id, enableHighAccuracy, timeout, maximumAge]);
-    return id;
-};
-
-/*
- * Native callback when watch position has a new position.
- * PRIVATE METHOD
- *
- * @param {String} id
- * @param {Number} lat
- * @param {Number} lng
- * @param {Number} alt
- * @param {Number} altacc
- * @param {Number} head
- * @param {Number} vel
- * @param {Number} stamp
- */
-Geolocation.prototype.success = function(id, lat, lng, alt, altacc, head, vel, stamp) {
-    var coords = new Coordinates(lat, lng, alt, altacc, head, vel);
-    var loc = new Position(coords, stamp);
-    try {
-        if (lat === "undefined" || lng === "undefined") {
-            navigator._geo.listeners[id].fail(new PositionError(PositionError.POSITION_UNAVAILABLE, "Lat/Lng are undefined."));
-        }
-        else {
-            navigator._geo.lastPosition = loc;
-            navigator._geo.listeners[id].success(loc);
-        }
-    }
-    catch (e) {
-        console.log("Geolocation Error: Error calling success callback function.");
-    }
-
-    if (id === "global") {
-        delete navigator._geo.listeners.global;
-    }
-};
-
-/**
- * Native callback when watch position has an error.
- * PRIVATE METHOD
- *
- * @param {String} id       The ID of the watch
- * @param {Number} code     The error code
- * @param {String} msg      The error message
- */
-Geolocation.prototype.fail = function(id, code, msg) {
-    try {
-        navigator._geo.listeners[id].fail(new PositionError(code, msg));
-    }
-    catch (e) {
-        console.log("Geolocation Error: Error calling error callback function.");
-    }
-};
-
-/**
- * Clears the specified heading watch.
- *
- * @param {String} id       The ID of the watch returned from #watchPosition
- */
-Geolocation.prototype.clearWatch = function(id) {
-    PhoneGap.exec(null, null, "Geolocation", "stop", [id]);
-    delete navigator._geo.listeners[id];
-};
-
-/**
- * Force the PhoneGap geolocation to be used instead of built-in.
- */
-Geolocation.usingPhoneGap = false;
-Geolocation.usePhoneGap = function() {
-    if (Geolocation.usingPhoneGap) {
-        return;
-    }
-    Geolocation.usingPhoneGap = true;
-
-    // Set built-in geolocation methods to our own implementations
-    // (Cannot replace entire geolocation, but can replace individual methods)
-    navigator.geolocation.setLocation = navigator._geo.setLocation;
-    navigator.geolocation.getCurrentPosition = navigator._geo.getCurrentPosition;
-    navigator.geolocation.watchPosition = navigator._geo.watchPosition;
-    navigator.geolocation.clearWatch = navigator._geo.clearWatch;
-    navigator.geolocation.start = navigator._geo.start;
-    navigator.geolocation.stop = navigator._geo.stop;
-};
-
-PhoneGap.addConstructor(function() {
-    navigator._geo = new Geolocation();
-
-    // No native geolocation object for Android 1.x, so use PhoneGap geolocation
-    if (typeof navigator.geolocation === 'undefined') {
-        navigator.geolocation = navigator._geo;
-        Geolocation.usingPhoneGap = true;
-    }
-});
-}
-
-
-/*
- * PhoneGap is available under *either* the terms of the modified BSD license *or* the
- * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- *
- * Copyright (c) 2005-2010, Nitobi Software Inc.
- * Copyright (c) 2010, IBM Corporation
- */
-
-
-/*
- * PhoneGap is available under *either* the terms of the modified BSD license *or* the
- * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- *
- * Copyright (c) 2005-2010, Nitobi Software Inc.
- * Copyright (c) 2010-2011, IBM Corporation
- */
-
-if (!PhoneGap.hasResource("media")) {
-PhoneGap.addResource("media");
-
-/**
- * This class provides access to the device media, interfaces to both sound and video
- *
- * @constructor
- * @param src                   The file name or url to play
- * @param successCallback       The callback to be called when the file is done playing or recording.
- *                                  successCallback() - OPTIONAL
- * @param errorCallback         The callback to be called if there is an error.
- *                                  errorCallback(int errorCode) - OPTIONAL
- * @param statusCallback        The callback to be called when media status has changed.
- *                                  statusCallback(int statusCode) - OPTIONAL
- * @param positionCallback      The callback to be called when media position has changed.
- *                                  positionCallback(long position) - OPTIONAL
- */
-var Media = function(src, successCallback, errorCallback, statusCallback, positionCallback) {
-
-    // successCallback optional
-    if (successCallback && (typeof successCallback !== "function")) {
-        console.log("Media Error: successCallback is not a function");
-        return;
-    }
-
-    // errorCallback optional
-    if (errorCallback && (typeof errorCallback !== "function")) {
-        console.log("Media Error: errorCallback is not a function");
-        return;
-    }
-
-    // statusCallback optional
-    if (statusCallback && (typeof statusCallback !== "function")) {
-        console.log("Media Error: statusCallback is not a function");
-        return;
-    }
-
-    // statusCallback optional
-    if (positionCallback && (typeof positionCallback !== "function")) {
-        console.log("Media Error: positionCallback is not a function");
-        return;
-    }
-
-    this.id = PhoneGap.createUUID();
-    PhoneGap.mediaObjects[this.id] = this;
-    this.src = src;
-    this.successCallback = successCallback;
-    this.errorCallback = errorCallback;
-    this.statusCallback = statusCallback;
-    this.positionCallback = positionCallback;
-    this._duration = -1;
-    this._position = -1;
-};
-
-// Media messages
-Media.MEDIA_STATE = 1;
-Media.MEDIA_DURATION = 2;
-Media.MEDIA_POSITION = 3;
-Media.MEDIA_ERROR = 9;
-
-// Media states
-Media.MEDIA_NONE = 0;
-Media.MEDIA_STARTING = 1;
-Media.MEDIA_RUNNING = 2;
-Media.MEDIA_PAUSED = 3;
-Media.MEDIA_STOPPED = 4;
-Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"];
-
-// TODO: Will MediaError be used?
-/**
- * This class contains information about any Media errors.
- * @constructor
- */
-var MediaError = function() {
-    this.code = null;
-    this.message = "";
-};
-
-MediaError.MEDIA_ERR_ABORTED        = 1;
-MediaError.MEDIA_ERR_NETWORK        = 2;
-MediaError.MEDIA_ERR_DECODE         = 3;
-MediaError.MEDIA_ERR_NONE_SUPPORTED = 4;
-
-/**
- * Start or resume playing audio file.
- */
-Media.prototype.play = function() {
-    PhoneGap.exec(null, null, "Media", "startPlayingAudio", [this.id, this.src]);
-};
-
-/**
- * Stop playing audio file.
- */
-Media.prototype.stop = function() {
-    return PhoneGap.exec(null, null, "Media", "stopPlayingAudio", [this.id]);
-};
-
-/**
- * Seek or jump to a new time in the track..
- */
-Media.prototype.seekTo = function(milliseconds) {
-    PhoneGap.exec(null, null, "Media", "seekToAudio", [this.id, milliseconds]);
-};
-
-/**
- * Pause playing audio file.
- */
-Media.prototype.pause = function() {
-    PhoneGap.exec(null, null, "Media", "pausePlayingAudio", [this.id]);
-};
-
-/**
- * Get duration of an audio file.
- * The duration is only set for audio that is playing, paused or stopped.
- *
- * @return      duration or -1 if not known.
- */
-Media.prototype.getDuration = function() {
-    return this._duration;
-};
-
-/**
- * Get position of audio.
- */
-Media.prototype.getCurrentPosition = function(success, fail) {
-    PhoneGap.exec(success, fail, "Media", "getCurrentPositionAudio", [this.id]);
-};
-
-/**
- * Start recording audio file.
- */
-Media.prototype.startRecord = function() {
-    PhoneGap.exec(null, null, "Media", "startRecordingAudio", [this.id, this.src]);
-};
-
-/**
- * Stop recording audio file.
- */
-Media.prototype.stopRecord = function() {
-    PhoneGap.exec(null, null, "Media", "stopRecordingAudio", [this.id]);
-};
-
-/**
- * Release the resources.
- */
-Media.prototype.release = function() {
-    PhoneGap.exec(null, null, "Media", "release", [this.id]);
-};
-
-/**
- * List of media objects.
- * PRIVATE
- */
-PhoneGap.mediaObjects = {};
-
-/**
- * Object that receives native callbacks.
- * PRIVATE
- * @constructor
- */
-PhoneGap.Media = function() {};
-
-/**
- * Get the media object.
- * PRIVATE
- *
- * @param id            The media object id (string)
- */
-PhoneGap.Media.getMediaObject = function(id) {
-    return PhoneGap.mediaObjects[id];
-};
-
-/**
- * Audio has status update.
- * PRIVATE
- *
- * @param id            The media object id (string)
- * @param status        The status code (int)
- * @param msg           The status message (string)
- */
-PhoneGap.Media.onStatus = function(id, msg, value) {
-    var media = PhoneGap.mediaObjects[id];
-    // If state update
-    if (msg === Media.MEDIA_STATE) {
-        if (value === Media.MEDIA_STOPPED) {
-            if (media.successCallback) {
-                media.successCallback();
-            }
-        }
-        if (media.statusCallback) {
-            media.statusCallback(value);
-        }
-    }
-    else if (msg === Media.MEDIA_DURATION) {
-        media._duration = value;
-    }
-    else if (msg === Media.MEDIA_ERROR) {
-        if (media.errorCallback) {
-            media.errorCallback(value);
-        }
-    }
-    else if (msg == Media.MEDIA_POSITION) {
-        media._position = value;
-    }
-};
-}
-
-
-/*
- * PhoneGap is available under *either* the terms of the modified BSD license *or* the
- * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- *
- * Copyright (c) 2005-2010, Nitobi Software Inc.
- * Copyright (c) 2010-2011, IBM Corporation
- */
-
-if (!PhoneGap.hasResource("network")) {
-PhoneGap.addResource("network");
-
-/**
- * This class contains information about the current network Connection.
- * @constructor
- */
-var Connection = function() {
-    this.type = null;
-    this._firstRun = true;
-    this._timer = null;
-    this.timeout = 500;
-
-    var me = this;
-    this.getInfo(
-        function(type) {
-            // Need to send events if we are on or offline
-            if (type == "none") {
-                // set a timer if still offline at the end of timer send the offline event
-                me._timer = setTimeout(function(){
-                    me.type = type;
-                    PhoneGap.fireEvent('offline');
-                    me._timer = null;
-                    }, me.timeout);
-            } else {
-                // If there is a current offline event pending clear it
-                if (me._timer != null) {
-                    clearTimeout(me._timer);
-                    me._timer = null;
-                }
-                me.type = type;
-                PhoneGap.fireEvent('online');
-            }
-            
-            // should only fire this once
-            if (me._firstRun) {
-                me._firstRun = false;
-                PhoneGap.onPhoneGapConnectionReady.fire();
-            }            
-        },
-        function(e) {
-            console.log("Error initializing Network Connection: " + e);
-        });
-};
-
-Connection.UNKNOWN = "unknown";
-Connection.ETHERNET = "ethernet";
-Connection.WIFI = "wifi";
-Connection.CELL_2G = "2g";
-Connection.CELL_3G = "3g";
-Connection.CELL_4G = "4g";
-Connection.NONE = "none";
-
-/**
- * Get connection info
- *
- * @param {Function} successCallback The function to call when the Connection data is available
- * @param {Function} errorCallback The function to call when there is an error getting the Connection data. (OPTIONAL)
- */
-Connection.prototype.getInfo = function(successCallback, errorCallback) {
-    // Get info
-    PhoneGap.exec(successCallback, errorCallback, "Network Status", "getConnectionInfo", []);
-};
-
-
-PhoneGap.addConstructor(function() {
-    if (typeof navigator.network === "undefined") {
-        navigator.network = new Object();
-    }
-    if (typeof navigator.network.connection === "undefined") {
-        navigator.network.connection = new Connection();
-    }
-});
-}
-
-
-/*
- * PhoneGap is available under *either* the terms of the modified BSD license *or* the
- * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- *
- * Copyright (c) 2005-2010, Nitobi Software Inc.
- * Copyright (c) 2010-2011, IBM Corporation
- */
-
-if (!PhoneGap.hasResource("notification")) {
-PhoneGap.addResource("notification");
-
-/**
- * This class provides access to notifications on the device.
- * @constructor
- */
-var Notification = function() {
-};
-
-/**
- * Open a native alert dialog, with a customizable title and button text.
- *
- * @param {String} message              Message to print in the body of the alert
- * @param {Function} completeCallback   The callback that is called when user clicks on a button.
- * @param {String} title                Title of the alert dialog (default: Alert)
- * @param {String} buttonLabel          Label of the close button (default: OK)
- */
-Notification.prototype.alert = function(message, completeCallback, title, buttonLabel) {
-    var _title = (title || "Alert");
-    var _buttonLabel = (buttonLabel || "OK");
-    PhoneGap.exec(completeCallback, null, "Notification", "alert", [message,_title,_buttonLabel]);
-};
-
-/**
- * Open a native confirm dialog, with a customizable title and button text.
- * The result that the user selects is returned to the result callback.
- *
- * @param {String} message              Message to print in the body of the alert
- * @param {Function} resultCallback     The callback that is called when user clicks on a button.
- * @param {String} title                Title of the alert dialog (default: Confirm)
- * @param {String} buttonLabels         Comma separated list of the labels of the buttons (default: 'OK,Cancel')
- */
-Notification.prototype.confirm = function(message, resultCallback, title, buttonLabels) {
-    var _title = (title || "Confirm");
-    var _buttonLabels = (buttonLabels || "OK,Cancel");
-    PhoneGap.exec(resultCallback, null, "Notification", "confirm", [message,_title,_buttonLabels]);
-};
-
-/**
- * Start spinning the activity indicator on the statusbar
- */
-Notification.prototype.activityStart = function() {
-    PhoneGap.exec(null, null, "Notification", "activityStart", ["Busy","Please wait..."]);
-};
-
-/**
- * Stop spinning the activity indicator on the statusbar, if it's currently spinning
- */
-Notification.prototype.activityStop = function() {
-    PhoneGap.exec(null, null, "Notification", "activityStop", []);
-};
-
-/**
- * Display a progress dialog with progress bar that goes from 0 to 100.
- *
- * @param {String} title        Title of the progress dialog.
- * @param {String} message      Message to display in the dialog.
- */
-Notification.prototype.progressStart = function(title, message) {
-    PhoneGap.exec(null, null, "Notification", "progressStart", [title, message]);
-};
-
-/**
- * Set the progress dialog value.
- *
- * @param {Number} value         0-100
- */
-Notification.prototype.progressValue = function(value) {
-    PhoneGap.exec(null, null, "Notification", "progressValue", [value]);
-};
-
-/**
- * Close the progress dialog.
- */
-Notification.prototype.progressStop = function() {
-    PhoneGap.exec(null, null, "Notification", "progressStop", []);
-};
-
-/**
- * Causes the device to blink a status LED.
- *
- * @param {Integer} count       The number of blinks.
- * @param {String} colour       The colour of the light.
- */
-Notification.prototype.blink = function(count, colour) {
-    // NOT IMPLEMENTED
-};
-
-/**
- * Causes the device to vibrate.
- *
- * @param {Integer} mills       The number of milliseconds to vibrate for.
- */
-Notification.prototype.vibrate = function(mills) {
-    PhoneGap.exec(null, null, "Notification", "vibrate", [mills]);
-};
-
-/**
- * Causes the device to beep.
- * On Android, the default notification ringtone is played "count" times.
- *
- * @param {Integer} count       The number of beeps.
- */
-Notification.prototype.beep = function(count) {
-    PhoneGap.exec(null, null, "Notification", "beep", [count]);
-};
-
-PhoneGap.addConstructor(function() {
-    if (typeof navigator.notification === "undefined") {
-        navigator.notification = new Notification();
-    }
-});
-}
-
-
-/*
- * PhoneGap is available under *either* the terms of the modified BSD license *or* the
- * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- *
- * Copyright (c) 2005-2010, Nitobi Software Inc.
- * Copyright (c) 2010-2011, IBM Corporation
- */
-
-if (!PhoneGap.hasResource("position")) {
-PhoneGap.addResource("position");
-
-/**
- * This class contains position information.
- * @param {Object} lat
- * @param {Object} lng
- * @param {Object} acc
- * @param {Object} alt
- * @param {Object} altacc
- * @param {Object} head
- * @param {Object} vel
- * @constructor
- */
-var Position = function(coords, timestamp) {
-       this.coords = coords;
-       this.timestamp = (timestamp !== 'undefined') ? timestamp : new Date().getTime();
-};
-
-/** @constructor */
-var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) {
-       /**
-        * The latitude of the position.
-        */
-       this.latitude = lat;
-       /**
-        * The longitude of the position,
-        */
-       this.longitude = lng;
-       /**
-        * The accuracy of the position.
-        */
-       this.accuracy = acc;
-       /**
-        * The altitude of the position.
-        */
-       this.altitude = alt;
-       /**
-        * The direction the device is moving at the position.
-        */
-       this.heading = head;
-       /**
-        * The velocity with which the device is moving at the position.
-        */
-       this.speed = vel;
-       /**
-        * The altitude accuracy of the position.
-        */
-       this.altitudeAccuracy = (altacc !== 'undefined') ? altacc : null;
-};
-
-/**
- * This class specifies the options for requesting position data.
- * @constructor
- */
-var PositionOptions = function() {
-       /**
-        * Specifies the desired position accuracy.
-        */
-       this.enableHighAccuracy = true;
-       /**
-        * The timeout after which if position data cannot be obtained the errorCallback
-        * is called.
-        */
-       this.timeout = 10000;
-};
-
-/**
- * This class contains information about any GSP errors.
- * @constructor
- */
-var PositionError = function() {
-       this.code = null;
-       this.message = "";
-};
-
-PositionError.UNKNOWN_ERROR = 0;
-PositionError.PERMISSION_DENIED = 1;
-PositionError.POSITION_UNAVAILABLE = 2;
-PositionError.TIMEOUT = 3;
-}
-
-
-/*
- * PhoneGap is available under *either* the terms of the modified BSD license *or* the
- * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
- *
- * Copyright (c) 2005-2010, Nitobi Software Inc.
- * Copyright (c) 2010-2011, IBM Corporation
- */
-
-/*
- * This is purely for the Android 1.5/1.6 HTML 5 Storage
- * I was hoping that Android 2.0 would deprecate this, but given the fact that
- * most manufacturers ship with Android 1.5 and do not do OTA Updates, this is required
- */
-
-if (!PhoneGap.hasResource("storage")) {
-PhoneGap.addResource("storage");
-
-/**
- * SQL result set object
- * PRIVATE METHOD
- * @constructor
- */
-var DroidDB_Rows = function() {
-    this.resultSet = [];    // results array
-    this.length = 0;        // number of rows
-};
-
-/**
- * Get item from SQL result set
- *
- * @param row           The row number to return
- * @return              The row object
- */
-DroidDB_Rows.prototype.item = function(row) {
-    return this.resultSet[row];
-};
-
-/**
- * SQL result set that is returned to user.
- * PRIVATE METHOD
- * @constructor
- */
-var DroidDB_Result = function() {
-    this.rows = new DroidDB_Rows();
-};
-
-/**
- * Storage object that is called by native code when performing queries.
- * PRIVATE METHOD
- * @constructor
- */
-var DroidDB = function() {
-    this.queryQueue = {};
-};
-
-/**
- * Callback from native code when query is complete.
- * PRIVATE METHOD
- *
- * @param id                Query id
- */
-DroidDB.prototype.completeQuery = function(id, data) {
-    var query = this.queryQueue[id];
-    if (query) {
-        try {
-            delete this.queryQueue[id];
-
-            // Get transaction
-            var tx = query.tx;
-
-            // If transaction hasn't failed
-            // Note: We ignore all query results if previous query
-            //       in the same transaction failed.
-            if (tx && tx.queryList[id]) {
-
-                // Save query results
-                var r = new DroidDB_Result();
-                r.rows.resultSet = data;
-                r.rows.length = data.length;
-                try {
-                    if (typeof query.successCallback === 'function') {
-                        query.successCallback(query.tx, r);
-                    }
-                } catch (ex) {
-                    console.log("executeSql error calling user success callback: "+ex);
-                }
-
-                tx.queryComplete(id);
-            }
-        } catch (e) {
-            console.log("executeSql error: "+e);
-        }
-    }
-};
-
-/**
- * Callback from native code when query fails
- * PRIVATE METHOD
- *
- * @param reason            Error message
- * @param id                Query id
- */
-DroidDB.prototype.fail = function(reason, id) {
-    var query = this.queryQueue[id];
-    if (query) {
-        try {
-            delete this.queryQueue[id];
-
-            // Get transaction
-            var tx = query.tx;
-
-            // If transaction hasn't failed
-            // Note: We ignore all query results if previous query
-            //       in the same transaction failed.
-            if (tx && tx.queryList[id]) {
-                tx.queryList = {};
-
-                try {
-                    if (typeof query.errorCallback === 'function') {
-                        query.errorCallback(query.tx, reason);
-                    }
-                } catch (ex) {
-                    console.log("executeSql error calling user error callback: "+ex);
-                }
-
-                tx.queryFailed(id, reason);
-            }
-
-        } catch (e) {
-            console.log("executeSql error: "+e);
-        }
-    }
-};
-
-/**
- * SQL query object
- * PRIVATE METHOD
- *
- * @constructor
- * @param tx                The transaction object that this query belongs to
- */
-var DroidDB_Query = function(tx) {
-
-    // Set the id of the query
-    this.id = PhoneGap.createUUID();
-
-    // Add this query to the queue
-    droiddb.queryQueue[this.id] = this;
-
-    // Init result
-    this.resultSet = [];
-
-    // Set transaction that this query belongs to
-    this.tx = tx;
-
-    // Add this query to transaction list
-    this.tx.queryList[this.id] = this;
-
-    // Callbacks
-    this.successCallback = null;
-    this.errorCallback = null;
-
-};
-
-/**
- * Transaction object
- * PRIVATE METHOD
- * @constructor
- */
-var DroidDB_Tx = function() {
-
-    // Set the id of the transaction
-    this.id = PhoneGap.createUUID();
-
-    // Callbacks
-    this.successCallback = null;
-    this.errorCallback = null;
-
-    // Query list
-    this.queryList = {};
-};
-
-/**
- * Mark query in transaction as complete.
- * If all queries are complete, call the user's transaction success callback.
- *
- * @param id                Query id
- */
-DroidDB_Tx.prototype.queryComplete = function(id) {
-    delete this.queryList[id];
-
-    // If no more outstanding queries, then fire transaction success
-    if (this.successCallback) {
-        var count = 0;
-        var i;
-        for (i in this.queryList) {
-            if (this.queryList.hasOwnProperty(i)) {
-                count++;
-            }
-        }
-        if (count === 0) {
-            try {
-                this.successCallback();
-            } catch(e) {
-                console.log("Transaction error calling user success callback: " + e);
-            }
-        }
-    }
-};
-
-/**
- * Mark query in transaction as failed.
- *
- * @param id                Query id
- * @param reason            Error message
- */
-DroidDB_Tx.prototype.queryFailed = function(id, reason) {
-
-    // The sql queries in this transaction have already been run, since
-    // we really don't have a real transaction implemented in native code.
-    // However, the user callbacks for the remaining sql queries in transaction
-    // will not be called.
-    this.queryList = {};
-
-    if (this.errorCallback) {
-        try {
-            this.errorCallback(reason);
-        } catch(e) {
-            console.log("Transaction error calling user error callback: " + e);
-        }
-    }
-};
-
-/**
- * Execute SQL statement
- *
- * @param sql                   SQL statement to execute
- * @param params                Statement parameters
- * @param successCallback       Success callback
- * @param errorCallback         Error callback
- */
-DroidDB_Tx.prototype.executeSql = function(sql, params, successCallback, errorCallback) {
-
-    // Init params array
-    if (typeof params === 'undefined') {
-        params = [];
-    }
-
-    // Create query and add to queue
-    var query = new DroidDB_Query(this);
-    droiddb.queryQueue[query.id] = query;
-
-    // Save callbacks
-    query.successCallback = successCallback;
-    query.errorCallback = errorCallback;
-
-    // Call native code
-    PhoneGap.exec(null, null, "Storage", "executeSql", [sql, params, query.id]);
-};
-
-var DatabaseShell = function() {
-};
-
-/**
- * Start a transaction.
- * Does not support rollback in event of failure.
- *
- * @param process {Function}            The transaction function
- * @param successCallback {Function}
- * @param errorCallback {Function}
- */
-DatabaseShell.prototype.transaction = function(process, errorCallback, successCallback) {
-    var tx = new DroidDB_Tx();
-    tx.successCallback = successCallback;
-    tx.errorCallback = errorCallback;
-    try {
-        process(tx);
-    } catch (e) {
-        console.log("Transaction error: "+e);
-        if (tx.errorCallback) {
-            try {
-                tx.errorCallback(e);
-            } catch (ex) {
-                console.log("Transaction error calling user error callback: "+e);
-            }
-        }
-    }
-};
-
-/**
- * Open database
- *
- * @param name              Database name
- * @param version           Database version
- * @param display_name      Database display name
- * @param size              Database size in bytes
- * @return                  Database object
- */
-var DroidDB_openDatabase = function(name, version, display_name, size) {
-    PhoneGap.exec(null, null, "Storage", "openDatabase", [name, version, display_name, size]);
-    var db = new DatabaseShell();
-    return db;
-};
-
-/**
- * For browsers with no localStorage we emulate it with SQLite. Follows the w3c api.
- * TODO: Do similar for sessionStorage.
- */
-
-/**
- * @constructor
- */
-var CupcakeLocalStorage = function() {
-               try {
-
-                       this.db = openDatabase('localStorage', '1.0', 'localStorage', 2621440);
-                       var storage = {};
-                       this.length = 0;
-                       function setLength (length) {
-                               this.length = length;
-                               localStorage.length = length;
-                       }
-                       this.db.transaction(
-                               function (transaction) {
-                                   var i;
-                                       transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
-                                       transaction.executeSql('SELECT * FROM storage', [], function(tx, result) {
-                                               for(var i = 0; i < result.rows.length; i++) {
-                                                       storage[result.rows.item(i)['id']] =  result.rows.item(i)['body'];
-                                               }
-                                               setLength(result.rows.length);
-                                               PhoneGap.initializationComplete("cupcakeStorage");
-                                       });
-
-                               },
-                               function (err) {
-                                       alert(err.message);
-                               }
-                       );
-                       this.setItem = function(key, val) {
-                               if (typeof(storage[key])=='undefined') {
-                                       this.length++;
-                               }
-                               storage[key] = val;
-                               this.db.transaction(
-                                       function (transaction) {
-                                               transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
-                                               transaction.executeSql('REPLACE INTO storage (id, body) values(?,?)', [key,val]);
-                                       }
-                               );
-                       };
-                       this.getItem = function(key) {
-                               return storage[key];
-                       };
-                       this.removeItem = function(key) {
-                               delete storage[key];
-                               this.length--;
-                               this.db.transaction(
-                                       function (transaction) {
-                                               transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
-                                               transaction.executeSql('DELETE FROM storage where id=?', [key]);
-                                       }
-                               );
-                       };
-                       this.clear = function() {
-                               storage = {};
-                               this.length = 0;
-                               this.db.transaction(
-                                       function (transaction) {
-                                               transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
-                                               transaction.executeSql('DELETE FROM storage', []);
-                                       }
-                               );
-                       };
-                       this.key = function(index) {
-                               var i = 0;
-                               for (var j in storage) {
-                                       if (i==index) {
-                                               return j;
-                                       } else {
-                                               i++;
-                                       }
-                               }
-                               return null;
-                       };
-
-               } catch(e) {
-                       alert("Database error "+e+".");
-                   return;
-               }
-};
-
-PhoneGap.addConstructor(function() {
-    var setupDroidDB = function() {
-        navigator.openDatabase = window.openDatabase = DroidDB_openDatabase;
-        window.droiddb = new DroidDB();
-    }
-    if (typeof window.openDatabase === "undefined") {
-        setupDroidDB();
-    } else {
-        window.openDatabase_orig = window.openDatabase;
-        window.openDatabase = function(name, version, desc, size){
-            // Some versions of Android will throw a SECURITY_ERR so we need 
-            // to catch the exception and seutp our own DB handling.
-            var db = null;
-            try {
-                db = window.openDatabase_orig(name, version, desc, size);
-            } 
-            catch (ex) {
-                db = null;
-            }
-
-            if (db == null) {
-                setupDroidDB();
-                return DroidDB_openDatabase(name, version, desc, size);
-            }
-            else {
-                return db;
-            }
-        }
-    }
-    
-    if (typeof window.localStorage === "undefined") {
-        navigator.localStorage = window.localStorage = new CupcakeLocalStorage();
-        PhoneGap.waitForInitialization("cupcakeStorage");
-    }
-});
-}
-
-
diff --git a/assets/www/js/view.js b/assets/www/js/view.js
deleted file mode 100644 (file)
index 6aa40cf..0000000
+++ /dev/null
@@ -1,486 +0,0 @@
-/*
- * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
- * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
- */
-
-var View = new function() {
-       var self = this;
-       //self.minOffset = 1000;
-       self.categories = {
-                       autor: 'Autorzy', 
-                       rodzaj: 'Rodzaje',
-                       gatunek: 'Gatunki',
-                       epoka: 'Epoki'
-       };
-       self.category_msc = {
-               autor: 'autorze',
-               rodzaj: 'rodzaju',
-               gatunek: 'gatunku',
-               epoka: 'epoce'
-       };
-       
-
-       self.init = function(success, error) {
-               console.log('View.init');
-
-               self._searchbox = document.getElementById("searchbox");
-               self._searchinput = document.getElementById("search");
-               self._content = document.getElementById("content");
-
-               self.current = '';
-               self.currentView = '';
-               self.currentPar = '';
-               self.currentTitle = '';
-
-               document.getElementById("cover").style.display = 'none';
-
-               self.checkNightMode();
-
-               self.at_spinner = false;
-               self.enter('');
-
-               success && success();
-       };
-
-
-       this.sanitize = function(text) {
-               return text.replace(/&/g, "&amp;").replace(/</g, "&lt;");
-       };
-
-       this.showSearch = function() {
-               self._searchbox.style.display = "block";
-       };
-
-       this.hideSearch = function() {
-               self._searchbox.style.display = "none";
-       };
-
-       this.spinner = function(text) {
-               if (!text)
-                       text = "Ładowanie";
-               if (self.at_spinner) {
-                       document.getElementById("spinnertext").innerHTML = text;
-               }
-               else {
-                       self._content.innerHTML = "<div class='spinner'><img src='img/spinner.png' /><div id='spinnertext'>" + text +"</div></div>";
-                       self.at_spinner = true;
-               }
-               setOffset(0);
-       };
-
-       this.content = function(text, offset) {
-               console.log('content');
-               self.at_spinner = false;
-
-               self._content.innerHTML = '';
-               self._content.innerHTML = text;
-               setOffset(offset);
-       }
-
-       this.enter = function(url, offset) {
-               console.log('View.enter: ' + url);
-
-               var view = 'Index';
-               var arg = null;
-
-               if (url.length) {
-                       var slash_index = url.indexOf('/');
-                       if (slash_index != -1) {
-                               view = url.substr(0, slash_index);
-                               arg = url.substr(slash_index + 1);
-                       }
-                       else {
-                               view = url;
-                       }
-               }
-               console.log('View.enter: ' + view + ' ' + arg);
-               self.current = url;
-               self.currentView = view;
-               self.currentPar = arg;
-               self['enter' + view](arg, offset);
-       }
-       
-       this.enterIndex = function(arg, offset) {
-               console.log('enterIndex');
-               Menu.setInfoButton("ProjectInfo", "Informacje o projekcie", true);
-               self.showSearch();
-               self.currentTitle = "Początek";
-               var html = "";
-
-               html += "<div class='buttons'>";
-               html += Links.button('Last', '', 'Ostatnio czytane');
-               html += Links.button('Bookmarks', '', 'Zakładki');
-
-               for (category in self.categories)
-                       html += Links.button('Category', category, self.categories[category], 0);
-               html += "</div>";
-               
-               html += "</div>" +"";
-                               /*"<p id='logo'><img src='img/logo-wl-nq8.png' alt='Wolne Lektury' /><br/>\n" +
-                               "szkolna biblioteka internetowa" +
-                               "</p>";*/
-               self.content(html, offset);
-       };
-       
-       this.enterBook = function(id, offset) {
-               id = parseInt(id);
-               console.log('enterBook: ' + id);
-               Menu.setInfoButton("BookInfo", "Informacje o utworze", true);
-               self.showSearch();
-
-               Catalogue.withBook(id, function(book) {
-                       self.currentTitle = book.authors + ', ' + book.title;
-
-                       Catalogue.withChildren(id, function(children) {
-                               var html = "<h1><span class='subheader'>";
-                               html += book.authors;
-                               html += "</span>" + book.title + "</h1>\n";
-                               if (book.html_file) {
-                                       html += "<div class='buttons'>" + Links.button('BookText', id, "Czytaj tekst") + "</div>";
-                               }
-                               if (children.length) {
-                                       html += "<div class='buttons'>";
-                                       for (c in children) {
-                                               child = children[c];
-                                               html += Links.bookLink(child);
-                                       }
-                                       html += "</div>";
-                               }
-                               self.content(html, offset);                             
-                       });
-               }, function() {
-                       History.goBack();
-               });
-       };
-       
-       this.enterBookText = function(id, offset) {
-               self.hideSearch();
-               self.spinner("Otwieranie utworu");
-               console.log('enterBookText: ' + id);
-               Menu.setInfoButton("BookInfo", "Informacje o utworze", true);
-               id = parseInt(id);
-
-               setTimeout("History.addRead("+id+");", 0);
-               
-               FileRepo.withHtml(id, function(data) {
-                       self.content(data, offset);
-               }, function(err) {
-                       alert("Błąd pobierania: nie udało się pobrać treści utworu.");
-                       History.goBack();
-               });
-               Catalogue.withBook(id, function(book) {
-                       self.currentTitle = book.authors + ', ' + book.title;
-               });
-       };
-
-
-       this.enterLast = function(ignored, offset) {
-               console.log("enterLast");
-               self.showSearch();
-               Menu.setInfoButton("ProjectInfo", "Informacje o projekcie", true);
-               self.currentTitle = 'Ostatnio czytane';
-               var html = "<h1><span class='subheader'>Ostatnio czytane</h1>\n";
-
-               var last_read = History.lastRead();
-               var some_books = false;
-
-               html += "<div class='buttons'>";
-               var add_books = function() {
-                       if (last_read.length) {
-                               var id = last_read.shift();
-                               Catalogue.withBook(id, function(book) {
-                                       html += Links.bookLink(book);
-                                       some_books = true;
-                                       add_books();
-                               }, function() {
-                                       add_books();
-                               });
-                       }
-                       else {
-                               if (!some_books) {
-                                       html += "<p>Nie przeczytano żadnych utworów.</p>";
-                               }
-                               html += "</div>";
-                               self.content(html, offset);
-                       }
-               };
-               add_books();
-       };
-
-
-       this.enterBookmarks = function(ignored, offset) {
-               console.log("enterBookmarks");
-               self.showSearch();
-               Menu.setInfoButton("ProjectInfo", "Informacje o projekcie", true);
-               self.currentTitle = 'Zakładki';
-               var html = "<h1><span class='subheader'>Zakładki</h1>\n";
-
-               var bookmarks = History.bookmarks();
-               if (!bookmarks.length) {
-                       html += "<p>Nie utworzono żadnych zakładek.</p>";
-                       self.content(html, offset);
-                       return;
-               }
-
-               html += "<div class='buttons bookmarks'>";
-               for (i in bookmarks) {
-                       var bm = bookmarks[i];
-
-                       var text = bm.name;
-                       text += "<div class='sub'>" + bm.title + "</div>";
-                       html += Links.deleteButton(bm.id);
-                       html += Links.button(bm.view, bm.par, text, bm.offset);
-               }
-               html += "</div>";
-               self.content(html, offset);
-       };
-
-       this.onBookmarkChange = function() {
-               // TODO: preserve offset
-               if (self.currentView == 'Bookmarks') {
-                       self.enterBookmarks();
-               }
-       };
-
-       this.enterTag = function(id, offset) {
-               id = parseInt(id);
-               console.log('enterTag: ' + id);
-               Menu.setInfoButton("TagInfo", "Informacje o...", true);
-               self.showSearch();
-
-               self.spinner("Otwieranie listy utworów");
-
-               Catalogue.withTag(id, function(tag) {
-                       Menu.setInfoButton("TagInfo", "Informacje o " + self.category_msc[tag.category], true);
-                       self.currentTitle = tag.category + ': ' + tag.name;
-                       var html = "<h1><span class='subheader upper'>" + tag.category + ': </span>' + tag.name + "</h1>\n";
-                       html += "<div class='buttons'>";
-                       if (tag.books) {
-                               Catalogue.withBooks(tag.books, function(books) {
-                                       for (var i in books) {
-                                               var book = books[i];
-                                               html += Links.bookLink(book);
-                                       }
-                                       html += "</div>";
-                                       self.content(html, offset);
-                               });
-                       }
-               }, function() {
-                       History.goBack();
-               });
-       };
-
-
-       this.enterCategory = function(category, offset) {
-               console.log('enterCategory: ' + category);
-               Menu.setInfoButton("ProjectInfo", "Informacje o projekcie", true);
-               self.spinner("Otwieranie katalogu");
-               self.showSearch();
-               self.currentTitle = self.categories[category];
-
-               Catalogue.withCategory(category, function(tags) {
-                       var html = "<h1>" + self.categories[category] + "</h1>\n";
-                       html += "<div class='buttons'>";
-                       for (i in tags) {
-                               tag = tags[i];
-                               html += Links.button('Tag', tag.id, tag.name);
-                       }
-                       html += "</div>";
-                       self.content(html, offset);
-               });
-       };
-
-
-       this.enterSearch = function(query, offset) {
-               console.log('enterSearch: ' + query);
-               Menu.setInfoButton("ProjectInfo", "Informacje o projekcie", true);
-               self.currentTitle = 'Szukaj: ' + query;
-               self.showSearch();
-
-               var html = "<h1><span class='subheader'>Szukana fraza:</span>" + View.sanitize(query) + "</h1>\n";
-
-               if (query.length < 2) {
-                       html += "<p>Szukana fraza musi mieć co najmniej dwa znaki</p>";
-                       self.content(html, offset);
-                       return;
-               }
-
-               Catalogue.withSearch(query, function(results) {
-                       if (results.length == 1) {
-                           var result = results[0];
-                           if (result.view == 'Book' && result.item.html_file) {
-                               self.enter(Links.href('BookText', result.item.id));
-                           }
-                           else {
-                                       self.enter(Links.href(result.view, result.item.id));
-                               }
-                               return;
-                       }
-                       if (results.length == 0) {
-                               html += "<p>Brak wyników wyszukiwania</p>";
-                       }
-                       else {
-                               html += "<div class='buttons'>";
-                               for (var i in results) {
-                                       var result = results[i];
-                                       if (result.view == 'Book')
-                                               html += Links.bookLink(result.item)
-                                       else
-                                               html += Links.button(result.view, result.item.id, result.item.name+"<div class='sub'>"+result.item.category+"</div>");
-                               }
-                               html += "</div>";
-                       }
-                       self.content(html, offset);
-               });
-       };
-
-
-       /* info */
-
-       this.enterProjectInfo = function(arg, offset) {
-               console.log('enterProjectInfo');
-               Menu.setInfoButton("ProjectInfo", "Informacje o projekcie", false);
-               self.hideSearch();
-               self.currentTitle = "Informacje o projekcie";
-
-               var html = "";
-
-               html += '<div class="info">';
-
-
-               html += "<p style='text-align:center'><img src='img/logo-wl.png' /></p>";
-               html += "<p>Biblioteka internetowa Wolne Lektury "+
-" udostępnia w swoich zbiorach lektury szkolne zalecane do użytku przez" + 
-" Ministerstwo Edukacji Narodowej i inne dzieła literatury.</p>";
-
-               html += "<p style='text-align:center'><img src='img/logo-fnp.png' /></p>";
-
-               html += "<img style='float:left;' src='img/procent.png' />" +
-                       "<p style='margin-left: 50px'>" + 
-                       "Przekaż 1% podatku na rozwój Wolnych Lektur.<br/>" +
-                       "Fundacja Nowoczesna Polska<br/>" +
-                       "KRS 0000070056</p>";
-
-               html += "<p>Większość pozycji w bibliotece należy do domeny publicznej "+
-                       "co oznacza, że nie są już chronione majatkowym prawem autorskim, "+
-                       "a więc można je swobodnie wykorzystywać, publikować i rozpowszechniać. "+
-                       "Publikujemy również kilka utworów, które autorzy udostępnili na wolnej licencji "+
-                       "<a href='http://creativecommons.org/licenses/by-sa/3.0/deed.pl'>"+
-                       "Creative Commons Uznanie Autorstwa - Na Tych Samych Warunkach 3.0.PL</a>.</p>";
-
-               html += "<p style='text-align:center'><img src='img/cc-by-sa.png' /></p>";
-
-               html += "<p>Copyright © 2011 Fundacja Nowoczesna Polska. Aplikacja jest wolnym oprogramowaniem "+
-                               "dostępnym na licencji GNU Affero GPL w wersji 3 lub późniejszej.</p>";
-
-               html += "<p>Więcej informacji o projekcie znajduje sie na stronie <a href='http://www.wolnelektury.pl'>http://www.wolnelektury.pl</a>.</p>";
-
-               html += '</div>';
-
-
-               self.content(html, offset);
-       };
-       
-       
-       this.enterBookInfo = function(id, offset) {
-               id = parseInt(id);
-               console.log('enterBookInfo: ' + id);
-               Menu.setInfoButton("ProjectInfo", "Informacje o projekcie", true);
-               self.hideSearch();
-
-               Catalogue.withBook(id, function(book) {
-                       self.currentTitle = "Informacje o: " + book.title;
-
-                       var html = '<h2>' + book.authors + ', ' + book.title + '</h2>';
-
-                       var url = WL + '/api/book/' + id + '/info.html';
-
-                       var xhr = new XMLHttpRequest();
-                       xhr.open("GET", url);
-                       xhr.onload = function() {
-                               console.log('BookInfo: fetched by ajax: ' + url);
-
-                               html += '<div class="info">';
-                               html += xhr.responseText;
-                               html += '</div>';
-
-                               self.content(html, offset);
-                       }
-                       xhr.onerror = function(e) {
-                               self.content("Brak informacji.", offset);
-                       }
-                       xhr.send();
-               }, function() {
-                       History.goBack();
-               });
-       };
-
-
-       this.enterTagInfo = function(id, offset) {
-               id = parseInt(id);
-               console.log('enterTagInfo: ' + id);
-               Menu.setInfoButton("ProjectInfo", "Informacje o projekcie", true);
-               self.hideSearch();
-
-               Catalogue.withTag(id, function(tag) {
-                       self.currentTitle = "Informacje o " + tag.name;
-                       var html = '<h2>' + tag.name + '</h2>';
-
-                       var url = WL + '/api/tag/' + id + '/info.html';
-
-                       var xhr = new XMLHttpRequest();
-                       xhr.open("GET", url);
-                       xhr.onload = function() {
-                               console.log('TagInfo: fetched by ajax: ' + url);
-
-                               html += '<div class="info">';
-                               html += xhr.responseText;
-                               html += '</div>';
-
-                               self.content(html, offset);
-                       }
-                       xhr.onerror = function(e) {
-                               self.content("Brak informacji.", offset);
-                       }
-                       xhr.send();
-               }, function() {
-                       History.goBack();
-               });
-       };
-
-
-       /* search form submit callback */
-       this.search = function() {
-               History.visit('Search/' + self._searchinput.value);
-               return false;
-       }
-       
-
-       self.getNightMode = function() {
-               night_mode = window.localStorage.getItem('View.night_mode');
-               if (night_mode === undefined)
-                       return false;
-               else
-                       return !!night_mode;
-       };
-
-       self.checkNightMode = function() {
-       night_mode = self.getNightMode();
-               if (night_mode) {
-                       document.body.setAttribute("class", "night-mode");
-               }
-               else {
-                       document.body.setAttribute("class", "");
-               }
-       };
-
-       self.setNightMode = function(night_mode) {
-               night_mode = night_mode ? "1" :  "";
-       window.localStorage.setItem('View.night_mode', night_mode);
-       self.checkNightMode();
-       };
-
-       self.toggleNightMode = function(night_mode) {
-               self.setNightMode(!self.getNightMode());
-       };
-}
diff --git a/default.properties b/default.properties
deleted file mode 100644 (file)
index 46769a7..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system use,
-# "build.properties", and override values to adapt the script to your
-# project structure.
-
-# Project target.
-target=android-7
diff --git a/gen/pl/org/nowoczesnapolska/wlmobi/R.java b/gen/pl/org/nowoczesnapolska/wlmobi/R.java
deleted file mode 100644 (file)
index aab3d7c..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/* AUTO-GENERATED FILE.  DO NOT MODIFY.
- *
- * This class was automatically generated by the
- * aapt tool from the resource data it found.  It
- * should not be modified by hand.
- */
-
-package pl.org.nowoczesnapolska.wlmobi;
-
-public final class R {
-    public static final class attr {
-    }
-    public static final class drawable {
-        public static final int icon=0x7f020000;
-    }
-    public static final class layout {
-        public static final int main=0x7f030000;
-    }
-    public static final class string {
-        public static final int app_name=0x7f050001;
-        public static final int hello=0x7f050000;
-    }
-    public static final class xml {
-        public static final int plugins=0x7f040000;
-    }
-}
diff --git a/libs/phonegap-1.0.0.jar b/libs/phonegap-1.0.0.jar
deleted file mode 100644 (file)
index b0b0486..0000000
Binary files a/libs/phonegap-1.0.0.jar and /dev/null differ
diff --git a/main.m b/main.m
new file mode 100644 (file)
index 0000000..f4289ab
--- /dev/null
+++ b/main.m
@@ -0,0 +1,17 @@
+//
+//  main.m
+//  wl-mobi
+//
+//  Created by FNP on 10/19/11.
+//  Copyright __MyCompanyName__ 2011. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+int main(int argc, char *argv[]) {
+    
+    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+    int retVal = UIApplicationMain(argc, argv, nil, @"AppDelegate");
+    [pool release];
+    return retVal;
+}
diff --git a/proguard.cfg b/proguard.cfg
deleted file mode 100644 (file)
index 12dd039..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
--optimizationpasses 5
--dontusemixedcaseclassnames
--dontskipnonpubliclibraryclasses
--dontpreverify
--verbose
--optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-
--keep public class * extends android.app.Activity
--keep public class * extends android.app.Application
--keep public class * extends android.app.Service
--keep public class * extends android.content.BroadcastReceiver
--keep public class * extends android.content.ContentProvider
--keep public class * extends android.app.backup.BackupAgentHelper
--keep public class * extends android.preference.Preference
--keep public class com.android.vending.licensing.ILicensingService
-
--keepclasseswithmembernames class * {
-    native <methods>;
-}
-
--keepclasseswithmembernames class * {
-    public <init>(android.content.Context, android.util.AttributeSet);
-}
-
--keepclasseswithmembernames class * {
-    public <init>(android.content.Context, android.util.AttributeSet, int);
-}
-
--keepclassmembers enum * {
-    public static **[] values();
-    public static ** valueOf(java.lang.String);
-}
-
--keep class * implements android.os.Parcelable {
-  public static final android.os.Parcelable$Creator *;
-}
diff --git a/res/drawable-hdpi/icon.png b/res/drawable-hdpi/icon.png
deleted file mode 100644 (file)
index d76c57a..0000000
Binary files a/res/drawable-hdpi/icon.png and /dev/null differ
diff --git a/res/drawable-ldpi/icon.png b/res/drawable-ldpi/icon.png
deleted file mode 100644 (file)
index 0f2180d..0000000
Binary files a/res/drawable-ldpi/icon.png and /dev/null differ
diff --git a/res/drawable-mdpi/icon.png b/res/drawable-mdpi/icon.png
deleted file mode 100644 (file)
index 128dcce..0000000
Binary files a/res/drawable-mdpi/icon.png and /dev/null differ
diff --git a/res/layout/main.xml b/res/layout/main.xml
deleted file mode 100644 (file)
index 3a5f117..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
-    >
-<TextView  
-    android:layout_width="fill_parent" 
-    android:layout_height="wrap_content" 
-    android:text="@string/hello"
-    />
-</LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
deleted file mode 100644 (file)
index f2d4fb8..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <string name="hello">Hello World, Catalogue!</string>
-    <string name="app_name">Wolne Lektury</string>
-</resources>
diff --git a/res/xml/plugins.xml b/res/xml/plugins.xml
deleted file mode 100644 (file)
index 2d8ba88..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<plugins>
-    <plugin name="App" value="com.phonegap.App"/>
-    <plugin name="Geolocation" value="com.phonegap.GeoBroker"/>
-    <plugin name="Device" value="com.phonegap.Device"/>
-    <plugin name="Accelerometer" value="com.phonegap.AccelListener"/>
-    <plugin name="Compass" value="com.phonegap.CompassListener"/>
-    <plugin name="Media" value="com.phonegap.AudioHandler"/>
-    <plugin name="Camera" value="com.phonegap.CameraLauncher"/>
-    <plugin name="Contacts" value="com.phonegap.ContactManager"/>
-    <plugin name="Crypto" value="com.phonegap.CryptoHandler"/>
-    <plugin name="File" value="com.phonegap.FileUtils"/>
-    <plugin name="Network Status" value="com.phonegap.NetworkManager"/>
-    <plugin name="Notification" value="com.phonegap.Notification"/>
-    <plugin name="Storage" value="com.phonegap.Storage"/>
-    <plugin name="Temperature" value="com.phonegap.TempListener"/>
-    <plugin name="FileTransfer" value="com.phonegap.FileTransfer"/>
-    <plugin name="Capture" value="com.phonegap.Capture"/>
-
-       <plugin name="Downloader" value="pl.org.nowoczesnapolska.wlmobi.Downloader" />
-       <plugin name="DBPut" value="pl.org.nowoczesnapolska.wlmobi.DBPut" />
-       <plugin name="MenuInterface" value="pl.org.nowoczesnapolska.wlmobi.MenuInterface" />
-</plugins>
\ No newline at end of file
diff --git a/src/pl/org/nowoczesnapolska/wlmobi/Catalogue.java b/src/pl/org/nowoczesnapolska/wlmobi/Catalogue.java
deleted file mode 100644 (file)
index be22a34..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
- * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
- */
-
-package pl.org.nowoczesnapolska.wlmobi;
-
-import android.os.Bundle;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.webkit.WebSettings;
-
-import com.phonegap.*;
-
-public class Catalogue extends DroidGap {
-
-       String infoLabel = "Proszę czekać";
-       Boolean infoEnabled = false;
-
-    /** Called when the activity is first created. */
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-
-        super.loadUrl("file:///android_asset/www/index.html");
-
-        MenuInterface.view = appView;
-
-        WebSettings settings = this.appView.getSettings();
-        settings.setSupportZoom(true);
-        settings.setBuiltInZoomControls(true);
-    }
-
-       @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-       menu.add(Menu.NONE, 1, 1, "Początek");
-       menu.add(Menu.NONE, 3, 2, "Dodaj zakładkę");
-       menu.add(Menu.NONE, 2, 3, MenuInterface.infoLabel);
-       menu.add(Menu.NONE, 4, 4, "Tryb nocny");
-        //MenuInflater inflater = getMenuInflater();
-        //inflater.inflate(R.menu.game_menu, menu);
-        return super.onCreateOptionsMenu(menu);
-    }
-
-    @Override
-    public boolean onPrepareOptionsMenu(Menu menu) {
-       MenuItem mi = menu.getItem(1);
-       mi.setTitle(MenuInterface.infoLabel);
-       mi.setEnabled(MenuInterface.infoEnabled);
-       return super.onPrepareOptionsMenu(menu);
-    }
-
-
-    @Override
-    public boolean onKeyDown(int i,KeyEvent e){
-       if (e.getKeyCode() == KeyEvent.KEYCODE_MENU) {
-               return false;
-       }
-        return super.onKeyDown(i, e);
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-       switch (item.getItemId()) {
-       case 1:
-               this.appView.loadUrl("javascript:Menu.start();");
-               break;
-       case 2:
-               this.appView.loadUrl("javascript:Menu.info();");
-               break;
-       case 3:
-               this.appView.loadUrl("javascript:Menu.bookmark();");
-               break;
-       case 4:
-               this.appView.loadUrl("javascript:Menu.toggleNightMode();");
-               break;
-       default:
-               return super.onOptionsItemSelected(item);
-       }
-       return true;
-    }
-}
\ No newline at end of file
diff --git a/src/pl/org/nowoczesnapolska/wlmobi/DBPut.java b/src/pl/org/nowoczesnapolska/wlmobi/DBPut.java
deleted file mode 100644 (file)
index d339614..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
- * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
- */
-
-package pl.org.nowoczesnapolska.wlmobi;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-
-import android.util.Log;
-import android.content.res.AssetManager;
-
-import com.phonegap.api.Plugin;
-import com.phonegap.api.PluginResult;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.io.IOException;
-import pl.org.nowoczesnapolska.wlmobi.Downloader;
-
-public class DBPut extends Plugin{
-
-       @Override
-       public PluginResult execute(String action, JSONArray args, String callbackId) {
-               if (action.equals("put")) {
-                       try {
-                               return this.put(args.getString(0), args.getString(1), args.getString(2));
-                       } catch (JSONException e) {
-                               return new PluginResult(PluginResult.Status.ERROR, "Param errrors");
-                       }
-               }
-               else if (action.equals("fetch")) {
-                       try {
-                               return this.fetch(args.getString(0));
-                       } catch (JSONException e) {
-                               return new PluginResult(PluginResult.Status.ERROR, "Param errrors");
-                       }
-               }
-               else {
-                       return new PluginResult(PluginResult.Status.INVALID_ACTION);
-               }
-       }
-
-       private PluginResult fetch(String url) {
-               String fileName = "0000000000000001.db";
-               String targetPath = "/data/data/" + this.ctx.getPackageName() + "/app_database/file__0/";
-               
-               Log.d("DBPut", "database path: " + targetPath + " / " + fileName);
-               
-               Downloader d = new Downloader();
-               return d.downloadUrl(url, targetPath, fileName, "true");
-       }
-
-
-       private PluginResult put(String assetPath, String targetPath, String overwrite) {
-               // this hard-coding is kinda creepy, should probably create the db and use getDatabasePath instead
-               String absoluteTargetPath = "/data/data/" + this.ctx.getPackageName() + "/app_database/" + targetPath;
-               int index = absoluteTargetPath.lastIndexOf('/');
-               String targetDir = absoluteTargetPath.substring(0, index);
-
-               try {
-                       File dir = new File(targetDir);
-                       if(!dir.exists()) {
-                               Log.d("DBPut", "directory " + targetDir + " created");
-                               dir.mkdirs();
-                       }
-
-                       File fout = new File(absoluteTargetPath);
-
-                       if(overwrite.equals("false") && fout.exists()) {
-                               Log.d("DBPut", "File already exists");
-                               return new PluginResult(PluginResult.Status.OK, "exist");
-                       }
-
-                       FileOutputStream fos = new FileOutputStream(fout);
-
-                       AssetManager assetManager = this.ctx.getResources().getAssets();
-                       InputStream is = assetManager.open(assetPath);
-
-                       byte[] buffer = new byte[1024];
-                       int len1 = 0;
-
-                       while ( (len1 = is.read(buffer)) > 0 ) {
-                               fos.write(buffer,0, len1);
-                       }
-
-                       fos.close();
-
-                       Log.d("DBPut", "Copied to " + absoluteTargetPath);
-               } catch (IOException e) {
-                       Log.d("DBPut", "Error: " + e);
-                       return new PluginResult(PluginResult.Status.ERROR, "Error: " + e);
-               }
-               return new PluginResult(PluginResult.Status.OK, absoluteTargetPath);
-       }
-}
\ No newline at end of file
diff --git a/src/pl/org/nowoczesnapolska/wlmobi/Downloader.java b/src/pl/org/nowoczesnapolska/wlmobi/Downloader.java
deleted file mode 100644 (file)
index 459891b..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-package pl.org.nowoczesnapolska.wlmobi;
-/*
- @author Mauro Rocco http://www.toforge.com
- Radek Czajka: don't prepend /sdcard/
-*/
-import org.json.JSONArray;
-import org.json.JSONException;
-import android.util.Log;
-import com.phonegap.api.Plugin;
-import com.phonegap.api.PluginResult;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-public class Downloader extends Plugin{
- @Override
- public PluginResult execute(String action, JSONArray args, String callbackId) {
- if (action.equals("downloadFile")) {
- try {
- return this.downloadUrl(args.getString(0),args.getString(1),args.getString(2),args.getString(3));
- } catch (JSONException e) {
- return new PluginResult(PluginResult.Status.ERROR, "Param errrors");
- }
- }
- else {
- return new PluginResult(PluginResult.Status.INVALID_ACTION);
- }
- }
- PluginResult downloadUrl(String fileUrl, String dirName, String fileName, String overwrite){
- try{
- Log.d("DownloaderPlugin", "DIRECTORY CALLED "+dirName+" created");
- File dir =     new File(dirName);
- if(!dir.exists()){
- Log.d("DownloaderPlugin", "directory "+dirName+" created");
- dir.mkdirs();
- }
- File file = new File(dirName+fileName);
- if(overwrite.equals("false") && file.exists()){
- Log.d("DownloaderPlugin", "File already exist");
- return new PluginResult(PluginResult.Status.OK, "exist");
- }
- URL url = new URL(fileUrl);
- Log.d("DownloaderPlugin", "connecting to server for downloading " + url);
- HttpURLConnection ucon = (HttpURLConnection) url.openConnection();
- ucon.setRequestMethod("GET");
- ucon.setDoOutput(true);
- ucon.connect();
- Log.d("DownloaderPlugin", "download begining");
- Log.d("DownloaderPlugin", "download url:" + url);
- InputStream is = ucon.getInputStream();
- byte[] buffer = new byte[1024];
- int len1 = 0;
- FileOutputStream fos = new FileOutputStream(file);
- while ( (len1 = is.read(buffer)) > 0 ) {
- fos.write(buffer, 0, len1);
-                //new String(buffer, "ISO8859_1").getBytes("UTF-8"), 0, len1);
- }
- fos.close();
- Log.d("DownloaderPlugin", "Download complete in" + fileName);
- } catch (IOException e) {
- Log.d("DownloaderPlugin", "Error: " + e);
- return new PluginResult(PluginResult.Status.ERROR, "Error: " + e);
- }
- return new PluginResult(PluginResult.Status.OK, fileName);
- }
-}
\ No newline at end of file
diff --git a/src/pl/org/nowoczesnapolska/wlmobi/MenuInterface.java b/src/pl/org/nowoczesnapolska/wlmobi/MenuInterface.java
deleted file mode 100644 (file)
index 064d4de..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
- * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
- */
-
-package pl.org.nowoczesnapolska.wlmobi;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-
-import android.graphics.Color;
-import android.webkit.WebView;
-
-import com.phonegap.api.Plugin;
-import com.phonegap.api.PluginResult;
-
-public class MenuInterface extends Plugin{
-
-       public static String infoLabel = "Proszę czekać...";
-       public static Boolean infoEnabled = false;
-       public static Boolean nightEnabled = false;
-       public static WebView view;
-
-       @Override
-       public PluginResult execute(String action, JSONArray args, String callbackId) {
-               if (action.equals("setInfoButton")) {
-                       try {
-                               return this.setInfoButton(args.getString(0), args.getString(1));
-                       } catch (JSONException e) {
-                               return new PluginResult(PluginResult.Status.ERROR, "Param errrors");
-                       }
-               }
-               else if (action.equals("setNightMode")) {
-                       try {
-                               return this.setNightMode(args.getString(0));
-                       } catch (JSONException e) {
-                               return new PluginResult(PluginResult.Status.ERROR, "Param errrors");
-                       }
-               }
-               else {
-                       return new PluginResult(PluginResult.Status.INVALID_ACTION);
-               }
-       }
-
-       private PluginResult setInfoButton(String label, String enabled) {
-       infoLabel = label;
-       infoEnabled = enabled.equals("true");
-
-               return new PluginResult(PluginResult.Status.OK);
-       }
-
-       private PluginResult setNightMode(String enabled) {
-       nightEnabled = enabled.equals("true");
-       if (nightEnabled) {
-               view.setBackgroundColor(0x222222ff);
-       }
-       else {
-               view.setBackgroundColor(Color.WHITE);
-       }
-
-               return new PluginResult(PluginResult.Status.OK);
-       }
-}
diff --git a/wl_mobi-Info.plist b/wl_mobi-Info.plist
new file mode 100644 (file)
index 0000000..52981a7
--- /dev/null
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleIconFiles</key>
+       <array>
+               <string>icon.png</string>
+               <string>icon@2x.png</string>
+               <string>icon-72.png</string>
+       </array>
+       <key>UISupportedInterfaceOrientations~ipad</key>
+       <array>
+               <string>UIInterfaceOrientationPortrait</string>
+               <string>UIInterfaceOrientationLandscapeLeft</string>
+               <string>UIInterfaceOrientationPortraitUpsideDown</string>
+               <string>UIInterfaceOrientationLandscapeRight</string>
+       </array>
+       <key>UISupportedInterfaceOrientations</key>
+       <array>
+               <string>UIInterfaceOrientationPortrait</string>
+               <string>UIInterfaceOrientationLandscapeLeft</string>
+               <string>UIInterfaceOrientationPortraitUpsideDown</string>
+               <string>UIInterfaceOrientationLandscapeRight</string>
+       </array>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>English</string>
+       <key>CFBundleDisplayName</key>
+       <string>${PRODUCT_NAME}</string>
+       <key>CFBundleExecutable</key>
+       <string>${EXECUTABLE_NAME}</string>
+       <key>CFBundleIconFile</key>
+       <string>icon.png</string>
+       <key>CFBundleIdentifier</key>
+       <string>pl.org.nowoczesnapolska.wl_mobi</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundleName</key>
+       <string>${PRODUCT_NAME}</string>
+       <key>CFBundlePackageType</key>
+       <string>APPL</string>
+       <key>CFBundleSignature</key>
+       <string>????</string>
+       <key>CFBundleVersion</key>
+       <string>1.0</string>
+       <key>LSRequiresIPhoneOS</key>
+       <true/>
+       <key>NSMainNibFile</key>
+       <string></string>
+       <key>NSMainNibFile~ipad</key>
+       <string></string>
+</dict>
+</plist>
diff --git a/wl_mobi-Prefix.pch b/wl_mobi-Prefix.pch
new file mode 100644 (file)
index 0000000..75ecfab
--- /dev/null
@@ -0,0 +1,8 @@
+//
+// Prefix header for all source files of the 'wl-mobi' target in the 'wl-mobi' project
+//
+
+#ifdef __OBJC__
+    #import <Foundation/Foundation.h>
+    #import <UIKit/UIKit.h>
+#endif
diff --git a/www/WolneLektury.html b/www/WolneLektury.html
new file mode 100644 (file)
index 0000000..858157a
--- /dev/null
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html style='width:100%'>
+<head>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=4, minimum-scale=.25, user-scalable=1;" />
+
+    <title>Wolne Lektury</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+    <script type="text/javascript" charset="utf-8" src="js/phonegap-1.1.0.js"></script>
+
+    <script type="text/javascript" charset="utf-8" src="js/NativeControls.js"></script>
+
+    <script type="text/javascript" charset="utf-8" src="js/dbput.js"></script>
+    <script type="text/javascript" charset="utf-8" src="js/downloader.js"></script>
+    <script type="text/javascript" charset="utf-8" src="js/menuinterface.js"></script>
+
+    <script type="text/javascript" charset="utf-8" src="js/main.js"></script>
+    <script type="text/javascript" charset="utf-8" src="js/catalogue.js"></script>
+    <script type="text/javascript" charset="utf-8" src="js/filerepo.js"></script>
+    <script type="text/javascript" charset="utf-8" src="js/history.js"></script>
+    <script type="text/javascript" charset="utf-8" src="js/links.js"></script>
+    <script type="text/javascript" charset="utf-8" src="js/menu.js"></script>
+    <script type="text/javascript" charset="utf-8" src="js/view.js"></script>
+
+    <link rel="stylesheet" type="style/css" href="css/style.css" />
+    <link rel="stylesheet" type="style/css" href="css/book_text.css" />
+</head>
+<body onload='onLoad()' style='width:100%'>
+
+
+<div id="top-bar">
+<button id="back-button" onclick='History.goBack()'>Wstecz</button>
+<div id="searchbox">
+    <form onsubmit="return View.search();">
+    <input id="searchbutton" type="submit" value="Szukaj" />
+    <div id="swrap"><input id="search" /></div>
+    </form>
+</div>
+</div>
+
+<img id="cover" src="img/cover.jpg" />
+<div id="content">
+<p class='footer'>Uruchamianie…</p>
+</div>
+
+</div>
+
+<div id='nothing'></div>
+</body>   
+</html>
\ No newline at end of file
diff --git a/www/css/book_text.css b/www/css/book_text.css
new file mode 100644 (file)
index 0000000..f9c7eff
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
+ * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+ */
+
+#book-text {
+       font-size: .75em;
+    line-height: 1.5em;
+    margin: 0;
+       max-width: 40em;
+}
+
+#book-text a {
+    color: blue;
+    text-decoration: none;
+}
+
+.night-mode #book-text a {
+    color: #88f;
+}
+
+
+/* ================================== */
+/* = Header with logo and menu      = */
+/* ================================== */
+#book-text #toc, #themes, #nota_red, #info {
+    display: none;
+}
+
+/* =================================================== */
+/* = Common elements: headings, paragraphs and lines = */
+/* =================================================== */
+#book-text h1 {
+    font-size: 2.5em;
+    margin: 1.5em 0;
+    text-align: center;
+    line-height: 1.5em;
+    font-weight: bold;
+}
+
+#book-text h2 {
+    font-size: 1.7em;
+    margin: 1.5em 0 0;
+    font-weight: bold;
+    line-height: 1.5em;
+}
+
+#book-text h3 {
+    font-size: 1.4em;
+    margin: 1.5em 0 0;
+    font-weight: normal;
+    line-height: 1.5em;
+}
+
+#book-text h4 {
+    font-size: 1em;
+    margin: 1.5em 0 0;
+    line-height: 1.5em;
+}
+
+#book-text p {
+    margin: 0;
+}
+
+/* ======================== */
+/* = Footnotes and themes = */
+/* ======================== */
+#book-text .theme-begin {
+       display:none;
+}
+
+#book-text .annotation {
+    font-style: normal;
+    font-weight: normal;
+    font-size: 12px;
+    padding-left: 2px;
+    position: relative;
+    top: -4px;
+}
+
+#book-text #footnotes {
+    margin-top: 3em;
+}
+
+#book-text #footnotes .annotation {
+    display: block;
+    float: left;
+    width: 2.5em;
+    clear: both;
+}
+
+#book-text #footnotes div {
+    margin: 1.5em 0 0 0;
+}
+
+#book-text #footnotes p, #footnotes ul {
+    margin-left: 2.5em;
+    font-size: 0.875em;
+}
+
+#book-text #footnotes .permalink {
+    font-size: .75em;
+}
+
+#book-text blockquote {
+    font-size: 0.875em;
+    margin: 0 0 0 1em;
+}
+
+/* ============= */
+/* = Numbering = */
+/* ============= */
+#book-text .anchor{display:none;}
+
+/* =================== */
+/* = Custom elements = */
+/* =================== */
+#book-text span.author {
+    font-size: 0.5em;
+    display: block;
+    line-height: 1.5em;
+    margin-bottom: 0.25em;
+}
+
+#book-text span.collection {
+    font-size: 0.375em;
+    display: block;
+    line-height: 1.5em;
+    margin-bottom: -0.25em;
+}
+
+#book-text span.subtitle {
+    font-size: 0.5em;
+    display: block;
+    line-height: 1.5em;
+    margin-top: -0.25em;
+}
+
+#book-text span.translator {
+    font-size: 0.375em;
+    display: block;
+    line-height: 1.5em;
+    margin-top: 0.25em;
+}
+
+#book-text div.didaskalia {
+    font-style: italic;
+    margin: 0.5em 0 0 1.5em;
+}
+
+#book-text div.kwestia {
+    margin: 0.5em 0 0;
+}
+
+#book-text div.stanza {
+    margin: 1.5em 0 0;
+}
+
+#book-text div.kwestia div.stanza {
+    margin: 0;
+}
+
+#book-text p.paragraph {
+    text-align: justify;
+    margin: 1.5em 0 0;
+}
+
+#book-text p.motto {
+    text-align: justify;
+    font-style: italic;
+    margin: 1.5em 0 0;
+}
+
+#book-text p.motto_podpis {
+    font-size: 0.875em;
+    text-align: right;
+}
+
+#book-text div.fragment {
+    border-bottom: 0.1em solid #999;
+    padding-bottom: 1.5em;
+}
+
+#book-text div.note p, div.dedication p, div.note p.paragraph, div.dedication p.paragraph {
+    text-align: right;
+    font-style: italic;
+}
+
+#book-text hr.spacer {
+    height: 3em;
+    visibility: hidden;
+}
+
+#book-text hr.spacer-line {
+    margin: 1.5em 0;
+    border: none;
+    border-bottom: 0.1em solid #000;
+}
+
+#book-text p.spacer-asterisk {
+    padding: 0;
+    margin: 1.5em 0;
+    text-align: center;
+}
+
+#book-text div.person-list ol {
+    list-style: none;
+    padding: 0 0 0 1.5em;
+}
+
+#book-text p.place-and-time {
+    font-style: italic;
+}
+
+#book-text em.math, em.foreign-word, em.book-title, em.didaskalia {
+    font-style: italic;
+}
+
+#book-text em.author-emphasis {
+    letter-spacing: 0.1em;
+}
+
+#book-text em.person {
+    font-style: normal;
+    font-variant: small-caps;
+}
+
+#book-text .verse:after {
+    content: "\feff";
+}
diff --git a/www/css/style.css b/www/css/style.css
new file mode 100644 (file)
index 0000000..03d2a3f
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
+ * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+ */
+
+body {
+       padding: 0;
+       margin: 0;
+       background: white;
+       font-family: verdana, arial, helvetica, sans-serif;
+}
+
+.night-mode, .night-mode #search {
+       color: #ddd;
+       background: #222;
+}
+.night-mode a {
+       color: #fff;
+}
+
+.spinner {
+       margin-top: 4em;
+       text-align: center;
+}
+@-webkit-keyframes rotate {
+  from {
+    -webkit-transform: rotate(0deg);
+  }
+  to { 
+    -webkit-transform: rotate(360deg);
+  }
+}
+.spinner img {
+       -webkit-animation-name: rotate;
+       -webkit-animation-duration: 4s;
+       -webkit-animation-iteration-count: 10;
+       -webkit-animation-direction: right;
+       -webkit-animation-timing-function:linear;
+}
+#spinnertext {
+       margin-top: 2em;
+       font-size: .7em;
+}
+
+#cover {
+       width: 100%;
+}
+
+#searchbox {
+       font-size: 1.25em;
+       /*background: #84bf2a;*/
+       padding: 3px 3px 6px 0;
+       display: none;
+}
+#search {
+       border: none;
+       width: 100%;
+       padding: 5px 0 5px 0;
+       font-size: 1em;
+}
+#swrap {
+       margin-right:85px;
+       margin-left:80px;
+}
+#searchbutton {
+       width:80px;
+       float:right;
+       margin-top:.5em;
+}
+#content {
+    padding: 10px;
+}
+.button {
+       display: block;
+       background: #ddd;
+    font-size: 1.25em;
+       padding: .5em;
+       margin-bottom: .5em;
+       border-radius: 8px;
+    -webkit-border-radius: 8px;
+       color: #295158;
+}
+.button img {
+       margin-right: .5em;
+       vertical-align: middle;
+}
+.button .sub {
+       font-size: .7em;
+}
+.button .note {
+       font-size: .5em;
+       text-align: right;
+}
+.button img {
+       float:left;
+}
+.button .label {
+       margin-left: 32px;
+}
+
+.button-Book {
+    background: #ccc;
+}
+.button-BookText {
+    background: #ccc;
+}
+.delete {
+       float: right;
+       width: 24px;
+
+       background: #464646;
+    font-size: 1.25em;
+       padding: .5em;
+       margin-bottom: .5em;
+       border-radius: 8px;
+    -webkit-border-radius: 8px;
+       color: white;
+       text-align: center;
+}
+
+h1 .subheader {
+       display:block;
+       font-size: 70%;
+}
+.upper {
+       text-transform: capitalize;
+}
+
+
+.footer {
+       font-size: 0.75em;
+       text-align: center;
+}
+
+.clr {
+       clear: both;
+}
+
+
+.info dt {
+       display: inline;
+       font-weight: bold;
+}
+
+.info dd {
+       display: inline;
+       margin: 0;
+}
+.info img {
+       max-width: 100%;
+}
+
+#top-bar {
+       background-image: -webkit-gradient(linear, left top, left bottom,
+               from(#7d828c),
+               to(#121a2e));
+}
+
+#back-button {
+       display:none;
+       float:left;
+       color: #fff;
+       text-decoration: none;
+       padding: 7px 10px;
+       -webkit-border-radius: 5px;
+
+ background-image: -webkit-gradient(linear, left top, left bottom, 
+        from(#7d828c),
+        color-stop(0.5, #303749), 
+        color-stop(0.5, #121a2e), 
+        to(#121a2e));
+ border: solid 1px rgba(79, 79, 79, 0.75);
+
+       position: relative;
+       padding-left: 5px;
+       margin-left: 1.5em;
+       margin-top: .5em;
+}
+
+
+#back-button:before {
+       content: " ";
+       display: block;
+       z-index: 0;
+       background-image: 
+               -webkit-gradient(linear, left top, right bottom, 
+        from(#7d828c),
+        color-stop(0.5, #303749), 
+        color-stop(0.5, #121a2e), 
+        to(#121a2e));
+       border-left: solid 1px #484e59;
+       border-bottom: solid 1px #9aa5bb;
+       -webkit-border-top-left-radius: 5px;
+       -webkit-border-bottom-right-radius: 5px;
+       -webkit-border-bottom-left-radius: 4px;
+       height: 23.5px;
+       width: 23.5px;
+       display: inline-block;
+       -webkit-transform: rotate(45deg);
+       -webkit-mask-image: 
+               -webkit-gradient(linear, left bottom, right top, 
+                       from(#000000), 
+                       color-stop(0.5,#000000), 
+                       color-stop(0.5, transparent), 
+                       to(transparent));
+       position: absolute;
+       left: -9px;
+       top: 2.5px;
+       -webkit-background-clip: content;
+}
diff --git a/www/img/cc-by-sa.png b/www/img/cc-by-sa.png
new file mode 100644 (file)
index 0000000..12c70b6
Binary files /dev/null and b/www/img/cc-by-sa.png differ
diff --git a/www/img/icon-Book.png b/www/img/icon-Book.png
new file mode 100644 (file)
index 0000000..3c18284
Binary files /dev/null and b/www/img/icon-Book.png differ
diff --git a/www/img/icon-BookText.png b/www/img/icon-BookText.png
new file mode 100644 (file)
index 0000000..60d38ee
Binary files /dev/null and b/www/img/icon-BookText.png differ
diff --git a/www/img/icon-Bookmarks.png b/www/img/icon-Bookmarks.png
new file mode 100644 (file)
index 0000000..4cb6b9c
Binary files /dev/null and b/www/img/icon-Bookmarks.png differ
diff --git a/www/img/icon-Last.png b/www/img/icon-Last.png
new file mode 100644 (file)
index 0000000..81d1b46
Binary files /dev/null and b/www/img/icon-Last.png differ
diff --git a/www/img/icon-Tag.png b/www/img/icon-Tag.png
new file mode 100644 (file)
index 0000000..db88d3a
Binary files /dev/null and b/www/img/icon-Tag.png differ
diff --git a/www/img/logo-fnp.png b/www/img/logo-fnp.png
new file mode 100644 (file)
index 0000000..8720140
Binary files /dev/null and b/www/img/logo-fnp.png differ
diff --git a/www/img/logo-wl.png b/www/img/logo-wl.png
new file mode 100644 (file)
index 0000000..a18e950
Binary files /dev/null and b/www/img/logo-wl.png differ
diff --git a/www/img/procent.png b/www/img/procent.png
new file mode 100644 (file)
index 0000000..2e0d73e
Binary files /dev/null and b/www/img/procent.png differ
diff --git a/www/img/spinner.png b/www/img/spinner.png
new file mode 100644 (file)
index 0000000..bad5a12
Binary files /dev/null and b/www/img/spinner.png differ
diff --git a/www/js/NativeControls.js b/www/js/NativeControls.js
new file mode 100644 (file)
index 0000000..6e6d3c3
--- /dev/null
@@ -0,0 +1,309 @@
+// JS ::::::::
+/*
+ //  This code is adapted from the work of:
+ //  Created by Michael Nachbaur on 13/04/09.
+ //  Copyright 2009 Decaf Ninja Software. All rights reserved.
+ //  MIT licensed
+ */
+/**
+ * This class exposes mobile phone interface controls to JavaScript, such as
+ * native tab and tool bars, etc.
+ * @constructor
+ */
+function NativeControls() {
+    this.tabBarTag = 0;
+    this.toolBarIndexes = 0;
+   
+    this.tabBarCallbacks = {};
+    this.toolBarCallbacks = {};
+   
+    this.tappedToolBarItem = null;
+    this.selectedTabBarItem = null;
+}
+/**
+ * Create a native tab bar that can have tab buttons added to it which can respond to events.
+ */
+NativeControls.prototype.createTabBar = function() {
+    PhoneGap.exec("NativeControls.createTabBar");
+};
+/**
+ * Show a tab bar.  The tab bar has to be created first.
+ * @param {Object} [options] Options indicating how the tab bar should be shown:
+ * - \c height integer indicating the height of the tab bar (default: \c 49)
+ * - \c position specifies whether the tab bar will be placed at the \c top or \c bottom of the screen (default: \c bottom)
+ */
+NativeControls.prototype.showTabBar = function(options) {
+    if (!options) options = {'position' : 'bottom'};
+    PhoneGap.exec("NativeControls.showTabBar", options);
+};
+/**
+ * Hide a tab bar.  The tab bar has to be created first.
+ */
+NativeControls.prototype.hideTabBar = function(animate) {
+    if (animate == undefined || animate == null)
+        animate = true;
+    PhoneGap.exec("NativeControls.hideTabBar", { animate: animate });
+};
+/**
+ * Create a new tab bar item for use on a previously created tab bar.  Use ::showTabBarItems to show the new item on the tab bar.
+ *
+ * If the supplied image name is one of the labels listed below, then this method will construct a tab button
+ * using the standard system buttons.  Note that if you use one of the system images, that the \c title you supply will be ignored.
+ *
+ * <b>Tab Buttons</b>
+ *   - tabButton:More
+ *   - tabButton:Favorites
+ *   - tabButton:Featured
+ *   - tabButton:TopRated
+ *   - tabButton:Recents
+ *   - tabButton:Contacts
+ *   - tabButton:History
+ *   - tabButton:Bookmarks
+ *   - tabButton:Search
+ *   - tabButton:Downloads
+ *   - tabButton:MostRecent
+ *   - tabButton:MostViewed
+ * @param {String} name internal name to refer to this tab by
+ * @param {String} [title] title text to show on the tab, or null if no text should be shown
+ * @param {String} [image] image filename or internal identifier to show, or null if now image should be shown
+ * @param {Object} [options] Options for customizing the individual tab item
+ *  - \c badge value to display in the optional circular badge on the item; if null or unspecified, the badge will be hidden
+ */
+NativeControls.prototype.createTabBarItem = function(name, label, image, options) {
+   
+        var tag = this.tabBarTag++;
+    if (options && 'onSelect' in options && typeof(options['onSelect']) == 'function') {
+        this.tabBarCallbacks[tag] = {'onSelect':options.onSelect,'name':name};
+        //delete options.onSelect;
+    }
+       
+    PhoneGap.exec("NativeControls.createTabBarItem", name, label, image, tag, options);
+};
+/**
+ * Update an existing tab bar item to change its badge value.
+ * @param {String} name internal name used to represent this item when it was created
+ * @param {Object} options Options for customizing the individual tab item
+ *  - \c badge value to display in the optional circular badge on the item; if null or unspecified, the badge will be hidden
+ */
+NativeControls.prototype.updateTabBarItem = function(name, options) {
+    if (!options) options = {};
+    PhoneGap.exec("NativeControls.updateTabBarItem", name, options);
+};
+/**
+ * Show previously created items on the tab bar
+ * @param {String} arguments... the item names to be shown
+ * @param {Object} [options] dictionary of options, notable options including:
+ *  - \c animate indicates that the items should animate onto the tab bar
+ * @see createTabBarItem
+ * @see createTabBar
+ */
+NativeControls.prototype.showTabBarItems = function() {
+    var parameters = [ "NativeControls.showTabBarItems" ];
+    for (var i = 0; i < arguments.length; i++) {
+        parameters.push(arguments[i]);
+    }
+    PhoneGap.exec.apply(this, parameters);
+};
+/**
+ * Function to detect currently selected tab bar item
+ * @see createTabBarItem
+ * @see showTabBarItems
+ */
+NativeControls.prototype.getSelectedTabBarItem = function() {
+    return this.selectedTabBarItem;
+};
+/**
+ * Manually select an individual tab bar item, or nil for deselecting a currently selected tab bar item.
+ * @param {String} tabName the name of the tab to select, or null if all tabs should be deselected
+ * @see createTabBarItem
+ * @see showTabBarItems
+ */
+NativeControls.prototype.selectTabBarItem = function(tab) {
+    PhoneGap.exec("NativeControls.selectTabBarItem", tab);
+};
+/**
+ * Function called when a tab bar item has been selected.
+ * @param {Number} tag the tag number for the item that has been selected
+ */
+NativeControls.prototype.tabBarItemSelected = function(tag)
+{
+        this.selectedTabBarItem = tag;
+    if (typeof(this.tabBarCallbacks[tag].onSelect) == 'function')
+        this.tabBarCallbacks[tag].onSelect(this.tabBarCallbacks[tag].name);
+};
+/**
+ * Create a toolbar.
+ */
+NativeControls.prototype.createToolBar = function()
+{
+    PhoneGap.exec("NativeControls.createToolBar");
+};
+/**
+ * Function called when a tab bar item has been selected.
+ * @param {String} title the title to set within the toolbar
+ */
+NativeControls.prototype.setToolBarTitle = function(title)
+{
+    PhoneGap.exec("NativeControls.setToolBarTitle", title);
+};
+/*
+ * Added by Emile khattar: emile818@gmail.com emile@sign.al
+ * @ 2011-07-08 ,  5.00 AM
+ */
+/**
+ * Set toolBarItems = nil;
+ */
+NativeControls.prototype.resetToolBar = function() {
+    PhoneGap.exec("NativeControls.resetToolBar");
+};
+/**
+ * Hide the tool bar
+ * @brief hide the tool bar
+ */
+NativeControls.prototype.hideToolBar = function() {
+    PhoneGap.exec("NativeControls.hideToolBar");
+};
+/**
+ * Show the tool bar ( re-render elements )
+ * @brief Show the tool bar
+ */
+NativeControls.prototype.showToolBar = function() {
+    PhoneGap.exec("NativeControls.showToolBar");
+};
+/**
+ * Set the toolbar title
+ * @param: title
+ */
+NativeControls.prototype.setToolBarTitle = function(title) {
+    PhoneGap.exec("NativeControls.setToolBarTitle" , title );
+};
+/**
+ * Create a new tool bar button item for use on a previously created tool bar.  Use ::showToolBar to show the new item on the tool bar.
+ *
+ * If the supplied image name is one of the labels listed below, then this method will construct a button
+ * using the standard system buttons.  Note that if you use one of the system images, that the title you supply will be ignored.
+ *
+ * <b>Tool Bar Buttons</b>
+ * UIBarButtonSystemItemDone
+ * UIBarButtonSystemItemCancel
+ * UIBarButtonSystemItemEdit
+ * UIBarButtonSystemItemSave
+ * UIBarButtonSystemItemAdd
+ * UIBarButtonSystemItemFlexibleSpace
+ * UIBarButtonSystemItemFixedSpace
+ * UIBarButtonSystemItemCompose
+ * UIBarButtonSystemItemReply
+ * UIBarButtonSystemItemAction
+ * UIBarButtonSystemItemOrganize
+ * UIBarButtonSystemItemBookmarks
+ * UIBarButtonSystemItemSearch
+ * UIBarButtonSystemItemRefresh
+ * UIBarButtonSystemItemStop
+ * UIBarButtonSystemItemCamera
+ * UIBarButtonSystemItemTrash
+ * UIBarButtonSystemItemPlay
+ * UIBarButtonSystemItemPause
+ * UIBarButtonSystemItemRewind
+ * UIBarButtonSystemItemFastForward
+ * UIBarButtonSystemItemUndo,        // iOS 3.0 and later
+ * UIBarButtonSystemItemRedo,        // iOS 3.0 and later
+ * UIBarButtonSystemItemPageCurl,    // iOS 4.0 and later
+ * @param {String} name internal name to refer to this tab by
+ * @param {String} [title] title text to show on the button, or null if no text should be shown
+ * @param {String} [image] image filename or internal identifier to show, or null if now image should be shown
+ * @param {Object} [options] Options for customizing the individual tab item [no option available at this time - this is for future proofing]
+ *  
+ */
+NativeControls.prototype.createToolBarItem = function(name , title , image , options) {
+        var toolBarIndex = this.toolBarIndexes++;
+        if (options && 'onTap' in options && typeof(options['onTap']) == 'function') {
+        this.toolBarCallbacks[toolBarIndex] = {'onTap':options.onTap,'name':name};
+        //delete options.onSelect;
+    }
+        //modify the NativeControls.m to change the options quickly
+        // the instance name on the plugin can be passed with option for now it is hardcode in objc // Emile
+    PhoneGap.exec("NativeControls.createToolBarItem" , name , title , image , options );
+};
+/**
+ * Function called when a tool bar item has been tapped.
+ * @param {Number} tag the tag number for the item that has been selected
+ */
+NativeControls.prototype.toolBarButtonTapped = function(tag)
+{
+        this.tappedToolBarItem = tag;
+    if (typeof(this.toolBarCallbacks[tag].onTap) == 'function')
+        this.toolBarCallbacks[tag].onTap(this.toolBarCallbacks[tag].name);
+};
+NativeControls.prototype.createActionSheet = function(buttonTitles,actionSheetTitle,cancelButtonIndex,destructiveButtonIndex)
+{
+        var options = {};
+       
+        if(actionSheetTitle != null)
+        {
+                options.title = actionSheetTitle;
+        }
+        if(cancelButtonIndex != null)
+        {
+                options.cancelButtonIndex = cancelButtonIndex;
+        }
+        if(destructiveButtonIndex != null)
+        {
+                options.destructiveButtonIndex = destructiveButtonIndex;
+        }
+   
+        var params = [ "NativeControls.createActionSheet",options ];
+    for (var i = 0; i < buttonTitles.length; i++)
+        {
+        params.push(buttonTitles[i]);
+    }
+    PhoneGap.exec.apply(this, params);
+       
+        this.actionSheetDelegate = {};
+        return this.actionSheetDelegate;
+}
+NativeControls.prototype._onActionSheetDismissed = function(index)
+{
+        this.actionSheetDelegate.onActionSheetDismissed(index);
+}
+NativeControls.prototype.setStatusBarVisibilty = function(bHide)
+{
+        PhoneGap.exec("StatusBar.setHidden",bHide);
+}
+if(!window.plugins)
+  window.plugins = {};
+ window.plugins.nativeControls = new NativeControls();
diff --git a/www/js/catalogue.js b/www/js/catalogue.js
new file mode 100644 (file)
index 0000000..40cd0bd
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
+ * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+ */
+
+var DB_VER = '0.9.17';
+
+var WL_INITIAL = WL + '/media/api/mobile/initial/initial.db';
+var WL_UPDATE = WL + '/api/changes/SINCE.json?book_fields=author,html,parent,parent_number,sort_key,title' +
+               '&tag_fields=books,category,name,sort_key' +
+               '&tag_categories=author,epoch,genre,kind';
+
+
+
+var categories = {'author': 'autor',
+              'epoch': 'epoka', 
+              'genre': 'gatunek', 
+              'kind': 'rodzaj', 
+              'theme': 'motyw'
+              }
+
+// FIXME: htmlescape strings!
+
+
+// for preparing sql statements
+// use like: 
+//   var s = new Sql("INSERT ... '{0}', '{1}' ...";
+//   s.prepare("abc", ...)
+var Sql = function(scheme) {
+       var self = this;
+       self.text = scheme;
+       
+       self.sql_escape = function(term) {
+               return term.toString().replace("'", "''");
+       };
+       
+       self.prepare = function() {
+               var args = arguments;
+               return self.text.replace(/{(\d+)}/g, function(match, number) {
+                       return self.sql_escape(args[parseInt(number)]);
+               });
+       }
+};
+
+
+var Catalogue = new function() {
+       /* API for database */
+
+       var self = this;
+       self.db = null;
+
+       this.init = function(success, error) {
+               debug('Catalogue.init');
+               
+               self.updateDB(function() {
+                       if (!self.db)
+                               self.db = window.openDatabase("wolnelektury", "1.0", "WL Catalogue", 1000000);
+                       if (self.db) {
+                               /*var regexp = {
+                                               onFunctionCall: function(val) {
+                                                       var re = new RegExp(val.getString(0));
+                                                               if (val.getString(1).match(re))
+                                                                       return 1;
+                                                               else
+                                                                       return 0;
+                                               }
+                                       };
+                               self.db.createFunction("REGEXP", 2, regexp);*/
+
+                               success && success();
+                       } else {
+                               error && error('Nie mogę otworzyć bazy danych: ' + err);
+                       }
+                       
+               }, function(err) {
+                       error && error('Błąd migracji: ' + err);
+               });
+       };
+
+       self.sqlSanitize = function(term) {
+               return term.toString().replace("'", "''");
+       };
+
+
+       /* check if DB needs updating and upload a fresh copy, if so */
+       this.updateDB = function(success, error) {
+               var has_ver = window.localStorage.getItem('db_ver');
+               if (has_ver == DB_VER) {
+                       debug('db ok, skipping')
+                       success && success();
+                       return;
+               }
+
+               var done = function() {
+                       FileRepo.clear();
+                       window.localStorage.setItem('db_ver', DB_VER);
+                       debug('db updated');
+                       success && success();
+               };
+
+               // db initialize
+               // this is Android-specific for now
+               self.createdb(done, error);
+       };
+
+
+       this.createdb = function(success, error) {
+               debug('create db');
+
+               var dbname = "wolnelektury";
+               var db = window.openDatabase(dbname, "1.0", "WL Catalogue", 1000000);
+               if (db) {
+                       debug('db created successfully');
+                       self.db = db;
+                       var sqls = [];
+                       sqls.push('CREATE TABLE IF NOT EXISTS book (\
+                                         id INTEGER PRIMARY KEY,\
+                                         title VARCHAR,\
+                                         html_file VARCHAR,\
+                                         html_file_size INTEGER,\
+                                         parent INTEGER,\
+                                         parent_number INTEGER,\
+                                         sort_key VARCHAR,\
+                                         pretty_size VARCHAR,\
+                                         authors VARCHAR,\
+                                         _local BOOLEAN\
+                                         );');
+                       sqls.push('CREATE INDEX IF NOT EXISTS book_title_index ON book (title);');
+                       sqls.push('CREATE INDEX IF NOT EXISTS book_sort_key_index ON book (sort_key);');
+                       sqls.push('CREATE INDEX IF NOT EXISTS book_parent_index ON book (parent);');
+                       sqls.push('CREATE TABLE IF NOT EXISTS tag (\
+                                         id INTEGER PRIMARY KEY,\
+                                         name VARCHAR,\
+                                         category VARCHAR,\
+                                         sort_key VARCHAR,\
+                                         books VARCHAR\
+                                         );');
+                       sqls.push('CREATE INDEX IF NOT EXISTS tag_name_index ON tag (name);');
+                       sqls.push('CREATE INDEX IF NOT EXISTS tag_category_index ON tag (category);');
+                       sqls.push('CREATE INDEX IF NOT EXISTS tag_sort_key_index ON tag (name);');
+                       sqls.push('CREATE TABLE IF NOT EXISTS state (last_checked INTEGER);');
+                       sqls.push('DELETE FROM state;');
+                       sqls.push('INSERT INTO state (last_checked) VALUES(0);');
+                       self.chainSqls(sqls, success, error);
+                       /*DBPut.fetch(WL_INITIAL, function(data) {
+                               debug('db fetch successful');
+                               success && success();
+                       }, function(data) {
+                               error && error('Błąd podczas pobierania bazy danych: ' + data);
+                       });*/
+               } else {
+                       error && error('Błąd podczas inicjowania bazy danych: ' + data);
+               }
+       };
+
+
+       this.withState = function(callback) {
+               self.db.transaction(function(tx) {
+                       tx.executeSql("SELECT * FROM state", [], 
+                               function(tx, results) {
+                                       if (results.rows.length) {
+                                               callback(results.rows.item(0));
+                                       }
+                                       else {
+                                               callback({last_checked: 0});
+                                       }
+                               });
+               });
+       };
+
+
+       this.withBook = function(id, callback, error) {
+               debug('withBook '+id)
+               self.db.transaction(function(tx) {
+                       tx.executeSql("SELECT * FROM book WHERE id="+id, [], 
+                               function(tx, results) {
+                                       if (results.rows.length) {
+                                               callback(results.rows.item(0));
+                                       }
+                                       else {
+                                               error && error();
+                                       }
+                               });
+               });
+       };
+
+       this.withBooks = function(ids, callback) {
+               debug('withBooks ' + ids)
+               self.db.transaction(function(tx) {
+                       tx.executeSql("SELECT * FROM book WHERE id IN ("+ids+") ORDER BY sort_key", [], 
+                               function(tx, results) {
+                                       var items = [];
+                                       var count = results.rows.length;
+                                       for (var i=0; i<count; ++i) {
+                                               items.push(results.rows.item(i));
+                                       }
+                                       callback(items);
+                               });
+               });
+       };
+
+
+       this.withChildren = function(id, callback) {
+               debug('withChildren ' + id)
+               self.db.transaction(function(tx) {
+                       tx.executeSql("SELECT * FROM book WHERE parent="+id+" ORDER BY parent_number, sort_key", [], 
+                               function(tx, results) {
+                                       var books = [];
+                                       var count = results.rows.length;
+                                       for (var i=0; i<count; ++i) {
+                                               books.push(results.rows.item(i));
+                                       }
+                                       callback(books);
+                       });
+               });
+       };
+
+       this.withTag = function(id, callback, error) {
+               debug('withTag '+id)
+               self.db.transaction(function(tx) {
+                       tx.executeSql("SELECT * FROM tag WHERE id="+id, [], 
+                               function(tx, results) {
+                                       if (results.rows.length) {
+                                               callback(results.rows.item(0));
+                                       }
+                                       else {
+                                               error && error();
+                                       }
+                               });
+               });
+       };
+
+       this.withCategory = function(category, callback) {
+               debug('withCategory ' + category)
+               self.db.transaction(function(tx) {
+                       tx.executeSql("SELECT * FROM tag WHERE category='"+category+"' ORDER BY sort_key", [], 
+                               function(tx, results) {
+                                       var items = [];
+                                       var count = results.rows.length;
+                                       for (var i=0; i<count; ++i)
+                                               items.push(results.rows.item(i));
+                                       callback(items);
+                               });
+               });
+       };
+
+
+       /* takes a query, returns a list of {view,id,label} objects to a callback */
+       this.withSearch = function(term, callback) {
+               debug('searching...');
+               term =  term.replace(/^\s+|\s+$/g, '') ;
+               var found = [];
+
+               function booksFound(tx, results) {
+                       var len = results.rows.length;
+                       debug('found books: ' + len);
+                       for (var i=0; i<len; i++) {
+                               var item = results.rows.item(i);
+                               found.push({
+                                       view: "Book",
+                                       item: item
+                               });
+                       }
+               };
+
+               function tagsFound(tx, results) {
+                       var len = results.rows.length;
+                       debug('found tags: ' + len);
+                       for (var i=0; i<len; i++) {
+                               var item = results.rows.item(i);
+                               found.push({
+                                       view: "Tag",
+                                       item: item
+                               });
+                       }
+                       // TODO error handling
+                       callback(found);
+               };
+
+
+               // FIXME escaping
+               // TODO pliterki, start of the word match
+               self.db.transaction(function(tx) {
+                       sql_term = self.sqlSanitize(term); // this is still insane, % and _
+                       tx.executeSql("SELECT * FROM book WHERE title LIKE '%"+sql_term+"%' ORDER BY sort_key LIMIT 10", [],
+                       //tx.executeSql("SELECT * FROM book WHERE title REGEXP '.*"+sql_term+".*' ORDER BY sort_key", [],
+                               function(tx, results) {
+                                       // save the books
+                                       booksFound(tx, results);
+                                       // and proceed to tags
+                                       tx.executeSql("SELECT * FROM tag WHERE name LIKE '%"+sql_term+"%' ORDER BY sort_key LIMIT 10",
+                                                       [], tagsFound);
+                               },
+                               function(err) {
+                                       debug('ERROR:search: '+err.code);
+                                       callback([]);
+                               });
+               });
+       };
+
+       self.chainSqls = function(sqls, success, error) {
+               self.db.transaction(function(tx) {
+                       var do_next = function() {
+                               if (sqls.length) {
+                                       var sql = sqls.shift();
+                                       debug(sql);
+                                       tx.executeSql(sql, [], do_next, error);
+                               }
+                               else {
+                                       success && success();
+                               }
+                       }
+                       do_next();
+               });
+       };
+
+
+       self.update = function(data, success, error) {
+               var addBookSql = new Sql("\
+                       INSERT OR REPLACE INTO book \
+                               (id, title, html_file,  html_file_size, parent, parent_number, sort_key, pretty_size, authors) \
+                       VALUES \
+                               ('{0}', '{1}', '{2}', '{3}', '{4}', '{5}', '{6}', '{7}', '{8}')");
+               var addTagSql = new Sql("INSERT OR REPLACE INTO tag (id, category, name, sort_key, books) VALUES ('{0}', '{1}', '{2}', '{3}', '{4}')");
+
+               var sqls = [];
+
+               if (data.deleted) {
+                       for (i in data.deleted.books) {
+                               var book_id = data.deleted.books[i];
+                               sqls.push("DELETE FROM book WHERE id=" + book_id);
+                               FileRepo.deleteIfExists(book_id);
+                       }
+
+                       for (i in data.deleted.tags) {
+                               var tag_id = data.deleted.tags[i];
+                               sqls.push("DELETE FROM tag WHERE id=" + tag_id);
+                       }
+               }
+
+               if (data.updated) {
+                       for (i in data.updated.books) {
+                               var book = data.updated.books[i];
+                               if (!book.html) book.html = {};
+                               if (!book.html.url) book.html.url = '';
+                               if (!book.html.size) book.html.size = '';
+                               if (!book.parent) book.parent = '';
+                               if (!book.parent_number) book.parent_number = '';
+                               var pretty_size = prettySize(book.html.size);
+                               sqls.push(addBookSql.prepare(
+                                       book.id, book.title, book.html.url, book.html.size,
+                                       book.parent, book.parent_number, book.sort_key, pretty_size, book.author
+                               ));
+                               FileRepo.deleteIfExists(book.id);
+                       }
+
+                       for (i in data.updated.tags) {
+                               var tag = data.updated.tags[i];
+                               var category = categories[tag.category];
+                               var books = tag.books.join(',');
+                               sqls.push(addTagSql.prepare(tag.id, category, tag.name, tag.sort_key, books));
+                       }
+               }
+
+               sqls.push("UPDATE state SET last_checked=" + data.time_checked);
+
+               self.chainSqls(sqls, success, error);
+       };
+
+
+       this.sync = function(success, error) {
+               self.withState(function(state) {
+                       var url = WL_UPDATE.replace("SINCE", state.last_checked); 
+                       debug('sync: ' + url);
+                       var xhr = new XMLHttpRequest();
+                       xhr.open("GET", url);
+                       xhr.onload = function() {
+                               debug('sync: fetched by ajax: ' + url);                 
+                               self.update(JSON.parse(xhr.responseText), success, error);
+                       }
+                       xhr.onerror = function(e) {
+                               error && error("Błąd aktualizacji bazy danych." + e);
+                       }
+                       xhr.send();
+               });
+               success && success();
+       };
+
+       this.updateLocal = function() {
+               FileRepo.withLocal(function(local) {
+                       self.db.transaction(function(tx) {
+                               tx.executeSql("UPDATE book SET _local=0", [], function(tx, results) {
+                                       ll = local.length;
+                                       var ids = [];
+                                       for (var i = 0; i < ll; i ++) {
+                                               ids.push(local[i].name);
+                                       }
+                                       ids = ids.join(',');
+                                       tx.executeSql("UPDATE book SET _local=1 where id in ("+ids+")"); 
+                               });
+                       });
+               }, function() {
+                       self.db.transaction(function(tx) {
+                               tx.executeSql("UPDATE book SET _local=0");
+                       });
+               });
+       };
+}
diff --git a/www/js/dbput.js b/www/js/dbput.js
new file mode 100644 (file)
index 0000000..457f613
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
+ * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+ */
+
+/**
+ *  
+ * @return Object literal singleton instance of DBPut
+ */
+var DBPut = { 
+       /**
+     * @param asset Path to the asset (relative to assets dir)
+     * @param target Path to DB file (relative to app db files dir)
+     * @param overwrite
+     * @param win Success callback
+     * @param fail Error callback
+     */
+       put: function(asset, target, overwrite, win, fail) {
+               if (overwrite==false) overwrite="false";
+               else overwrite="true";
+               return PhoneGap.exec(
+                       win, 
+                       fail, 
+                       "DBPut", 
+                       "put", 
+                       [asset, target, overwrite]
+               );
+       },
+       fetch: function(url, win, fail) {
+               return PhoneGap.exec(
+                       win, 
+                       fail, 
+                       "DBPut", 
+                       "fetch", 
+                       [url]
+               );
+       }
+};
+
diff --git a/www/js/downloader.js b/www/js/downloader.js
new file mode 100644 (file)
index 0000000..15edf30
--- /dev/null
@@ -0,0 +1,19 @@
+/**
+ *  
+ * @return Object literal singleton instance of Downloader
+ */
+var Downloader = { 
+       /**
+     * @param fileUrl
+     * @param dirName
+     * @param fileName
+     * @param overwrite
+     * @param win
+     * @param fail
+     */
+       downloadFile: function(fileUrl,dirName,fileName,overwrite,win,fail) {
+               if(overwrite==false) overwrite="false";
+               else overwrite="true";
+               return PhoneGap.exec(win, fail, "Downloader", "downloadFile", [fileUrl,dirName,fileName,overwrite]);
+       }
+};
diff --git a/www/js/filerepo.js b/www/js/filerepo.js
new file mode 100644 (file)
index 0000000..58b6809
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
+ * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+ */
+
+var FileRepo = new function() {
+       /* API for files repository */
+       var self = this;
+       this.root = null;
+
+       this.init = function(success, error) {
+               self.initRoot(success);
+       };
+
+       this.initRoot = function(success) {
+               // fs size is irrelevant, PERSISTENT is futile (on Android, at least)
+               window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, function(fs) {
+                       debug('local fs found: ' + fs.root.fullPath);
+                       self.root = fs.root;
+                       
+                       success && success();
+               }, function() {
+                       debug('local fs not found');
+                       success && success();
+               });
+       };
+
+
+       this.withLocalHtml = function(book_id, success, error) {
+               debug('info:withLocalHtml: id:' + book_id);
+               View.spinner('Otwieranie treści utworu');
+               if (!self.root)
+                       error && error('info:withLocalHtml: no local html: no usable filesystem');
+
+               var url = "file://" + self.root.fullPath + "/html/" + book_id;
+               debug('info:withLocalHtml: local ajax: ' + url);
+               var xhr = new XMLHttpRequest();
+               xhr.open('GET', url, true);
+               xhr.onload = function() {
+                       debug('info:withLocalHtml: fetched by local ajax: ' + url);
+                       success && success(xhr.responseText);
+               }
+               xhr.onerror = error;
+               xhr.send();
+       };
+
+
+       this.withLocal = function(win, fail) {
+               if (self.root) {
+                       self.root.getDirectory('html', {create:true}, function(dir) {
+                               var reader = dir.createReader();
+                               reader.readEntries(win, fail);
+                       });
+               }
+               else {
+                       win && win([]);
+               }
+       }
+
+
+       // downloads HTML file from server, saves it in cache and calls success with file contents
+       this.withHtmlFromServer = function(book_id, success, error) {
+               debug('info:withHtmlFromServer: id:' + book_id);
+               // read file from WL
+               Catalogue.withBook(book_id, function(book) {
+                       var url = WL + book.html_file;
+                       debug('info:withHtmlFromServer: fetching url: ' + url);
+
+                       View.spinner("Pobieranie treści utworu z sieci");
+
+                       var xhr = new XMLHttpRequest();
+                       xhr.open("GET", url);
+                       xhr.onload = function() {                       
+                               data = xhr.responseText;
+                               if(xhr.status != 200) {
+                                                  alert(xhr.status);
+                                       error && error('Błąd podczas pobierania pliku');
+                               } else {
+                                 if (self.root) {
+                                       self.root.getDirectory('html', {create: true}, function(dir) {
+                                                       var reader = dir.getFile(''+book_id, {create: true}, function(f) {
+                                                                                                        f.createWriter(function(w) {
+                                                                                                                                       w.write(data);
+                                                                                                                                       self.db.transaction(function(tx) {
+                                                                                                                                                                               alert('setlocal0');
+                                                                                                                                                                               tx.executeSql("UPDATE book SET _local=1 WHERE id="+book_id);
+                                                                                                                                                                               alert('setlocal1');
+                                                                                                                                                                               });
+                                                                                                                                       });
+                                                                                                        });
+                                                                                  });
+                                 }
+                                 success && success(data);
+                               }
+                       };
+                       xhr.onerror = function(e) {
+                               debug('Błąd podczas pobierania pliku')
+                               error && error("error: " + data);
+                       };
+                       xhr.send();
+               });             
+       };
+       
+       // calls the callback with contents of the HTML file for a given book,
+       // loaded from the server and cached locally
+       this.withHtml = function(id, success, error) {
+               debug('info:withHtml: id:' + id);
+               self.withLocalHtml(id, success, function() {
+                       self.withHtmlFromServer(id, success, error);
+               });
+       };
+
+
+       this.clear = function() {
+               FileRepo.withLocal(function(local) {
+                       for (i in local) {
+                               local[i].remove();
+                       }
+               });
+       };
+
+
+       this.deleteIfExists = function(id) {
+               if (self.root) {
+                       self.root.getFile('html/' + id, {create: false}, function(f) {
+                               f.remove();
+                       });
+               }
+       }
+};
diff --git a/www/js/history.js b/www/js/history.js
new file mode 100644 (file)
index 0000000..66531c9
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
+ * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+ */
+
+var History = new function() {
+       var self = this;
+
+       self.init = function(success, error) {
+               debug('History.init');
+
+               self.viewStack = [];
+               //navigator.app.overrideBackbutton(); FIXME!!!
+               //document.addEventListener("backbutton", History.goBack, true);
+
+               success && success();
+       };
+
+       self.visit = function(url, offset) {
+               offset = offset || 0;
+               self.viewStack.push(View.current);
+               View.showBack(true);
+               debug('History.visit: ' + url);
+               View.enter(url, offset);
+       };
+       
+       self.goBack = function() {
+               if (self.viewStack.length > 0) {
+                       var url = self.viewStack.pop();
+                       debug('History.goBack: ' + url);
+                       if (self.viewStack.length == 0)
+                               View.showBack(false);
+                       View.enter(url);
+               }
+               else {
+                       debug('History: exiting');
+                       navigator.app.exitApp();
+               }
+       };
+
+
+       self.lastRead = function() {
+               var last_read = window.localStorage.getItem('History.last_ids');
+               try {
+                       return last_read.split(';');
+               } catch (err) {
+                       return [];
+               }
+       };
+
+       self.addRead = function(id, offset) {
+               id = "" + id; // this should check if int
+               debug("History.addRead: " + id);
+               var last_read = self.lastRead();
+               var lastly = last_read.indexOf(id);
+               if (lastly != -1) {
+                       last_read.splice(lastly, 1);
+               }
+               while (last_read.length >= 10) {
+                       last_read.pop();
+               }
+               last_read.unshift(id);
+               window.localStorage.setItem('History.last_ids', last_read.join(';'));
+       }
+
+
+       self.bookmarks = function() {
+               var bookmarks = window.localStorage.getItem('History.bookmarks');
+               try {
+                       return JSON.parse(bookmarks) || [];
+               } catch (err) {
+                       return [];
+               }
+       };
+
+       self.addBookmark = function(name) {
+               var id=(new Date).getTime();
+               debug("History.addBookmark: " + id);
+
+               var bms = self.bookmarks();
+               bms.unshift({
+                       id: id,
+                       name: name,
+                       title: View.currentTitle,
+                       view: View.currentView,
+                       par: View.currentPar,
+                       offset: currentOffset()
+               });
+               window.localStorage.setItem('History.bookmarks', JSON.stringify(bms));
+       }
+
+       self.deleteBookmark = function(id) {
+               debug("History.deleteBookmark: " + id);
+               var bms = self.bookmarks();
+               for (b in bms) {
+                       if (bms[b].id == id) {
+                               bms.splice(b, 1);
+                       }
+               }
+               window.localStorage.setItem('History.bookmarks', JSON.stringify(bms));
+               View.onBookmarkChange();
+       }
+}
diff --git a/www/js/links.js b/www/js/links.js
new file mode 100644 (file)
index 0000000..3a7465a
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
+ * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+ */
+
+var Links = new function() {
+       var self = this;
+
+       self.href = function(view, par) {
+               return view+"/"+par;
+       };
+
+       self.button = function(view, par, text, offset) {
+               offset = offset || 0;
+               var html = "<div class='button button-"+view+"' onclick='History.visit(\"" + 
+                                       self.href(view, par).replace(/["']/g, "\\$&") + "\", "+offset+");'>";
+               icon = view;
+               if (icon != 'Book' && icon != 'Bookmarks' && icon != 'BookText' && 
+                       icon != 'Last' && icon != 'Tag') {
+                   icon = 'Tag';
+               }
+               html += "<img src='img/icon-" + icon + ".png' />";
+               html += "<div class='label'>" + text + "</div>";
+               html += "<div class='clr'></div>";
+               html += "</div>\n";
+               return html; 
+       };
+
+       self.bookLink = function(book) {
+               var target = 'Book';
+               var note = '';
+
+               if (book.html_file) {
+                       // this assumes that either book has a html XOR it has children
+                       target = 'BookText';
+                       note = "<div class='note'>";
+                       if (book._local)
+                               note += 'Pobrane';
+                       else {
+                               note += book.pretty_size;
+                       }
+                       note += "</div>";
+               }
+
+               return self.button(target, book.id,
+                               "<div class='sub'>" + book.authors + "</div>" +
+                               book.title + note);
+       };
+
+       self.deleteButton = function(id) {
+               return "<div class='delete' onClick='History.deleteBookmark(\""+id+"\");'>x</div>";
+       };
+}
diff --git a/www/js/main.js b/www/js/main.js
new file mode 100644 (file)
index 0000000..cf92353
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
+ * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+ */
+
+var WL = 'http://www.wolnelektury.pl';
+
+
+// disable debugging
+//debug = function(text) {};
+debug = function(text) {console.log(text);};
+
+
+function onLoad() {
+       debug('onLoad');
+       document.addEventListener("deviceready", onDeviceReady, false);
+}
+
+function onDeviceReady() {
+       debug('onDeviceReady');
+
+       var error = function(err) { alert(err); };
+
+       FileRepo.init(function() {
+               debug('after FileRepo.init');
+               Catalogue.init(function() {
+                       debug('after catalogue.init');
+                       History.init(function() {
+                               debug('after history.init');
+                               View.init(function() {
+                                       Menu.init(function() {
+                                               View.go();
+                                               Catalogue.sync(function() {
+                                                       Catalogue.updateLocal();
+                                               }, error);
+                                       }, error);
+                               }, error);
+                       }, error);
+               }, error);
+       });
+}
+
+
+var currentOffset = function() {
+       var scr = document.body.scrollTop;
+       var h = document.getElementById('nothing').offsetTop;
+       return scr/h;
+};
+
+var setOffset = function(offset) {
+       var h = document.getElementById('nothing').offsetTop;
+       setTimeout(function() {scroll(0, h*offset); }, 10);
+};
+
+
+var prettySize = function(size) {
+    if (!size) return "";
+    var units = ['B', 'KiB', 'MiB', 'GiB'];
+    size = size;
+    var unit = units.shift();
+    while (size > 1000 && units.length) {
+        size /= 1024;
+        unit = units.shift();
+    }
+    if (size < 10) {
+        return Math.round(size*10)/10 + ' ' + unit;
+    }
+    return Math.round(size) + ' ' + unit;
+};
+
diff --git a/www/js/menu.js b/www/js/menu.js
new file mode 100644 (file)
index 0000000..11f1be6
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
+ * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+ */
+
+var Menu = new function() {
+       var self = this;
+       var infoView = 'ProjectInfo';
+       var showInfo = true;
+
+       self.init = function(success) {
+               window.plugins.nativeControls.createTabBar(); 
+               window.plugins.nativeControls.createTabBarItem("start", "Początek","/www/img/icon-Last.png", {onSelect: Menu.start}); 
+               window.plugins.nativeControls.createTabBarItem("addmark","Dodaj zakładkę","/www/img/icon-Bookmarks.png", {onSelect: Menu.bookmark}); 
+               
+               //window.plugins.nativeControls.createTabBarItem("info",self.infoLabel,"/www/img/icon-Tag.png", {onSelect: Menu.info}); 
+               //window.plugins.nativeControls.createTabBarItem("night","Tryb nocny","", {onSelect: Menu.toggleNightMode}); 
+               //window.plugins.nativeControls.showTabBarItems("start", "addmark", "info", "night"); 
+               //window.plugins.nativeControls.showTabBar();
+               self.setInfoButton(self.infoView, 'O projekcie', self.showInfo);
+               self.setNightModeLabel();
+               success && success();
+       };
+
+       self.start = function() {
+               History.visit('');
+               window.plugins.nativeControls.selectTabBarItem('');
+       };
+
+       self.info = function() {
+               History.visit(self.infoView + '/' + View.currentPar);
+               window.plugins.nativeControls.selectTabBarItem('');
+       };
+
+       self.bookmark = function() {
+               var name = prompt('Nazwa zakładki');
+               if (name != null)
+                       History.addBookmark(name);
+               window.plugins.nativeControls.selectTabBarItem('');
+       };
+
+       self.setNightModeLabel = function() {
+               var label = 'Tryb nocny';
+               if (View.getNightMode()) label = 'Tryb dzienny';
+               window.plugins.nativeControls.createTabBarItem("night",label,"/www/img/icon-Tag.png", {onSelect: Menu.toggleNightMode}); 
+       };
+
+       self.refresh = function() {
+               var items = new Array();
+               if (View.currentView != 'Index')
+                       items.push('start');
+               items.push("addmark");
+               if (self.showInfo)
+                       items.push("info");
+               items.push("night");
+               window.plugins.nativeControls.showTabBarItems.apply(this, items);
+               window.plugins.nativeControls.showTabBar();
+       };
+
+       self.toggleNightMode = function() {
+               View.toggleNightMode();
+               self.setNightModeLabel();
+       };
+
+       self.setInfoButton = function(view, label, enabled) {
+               self.infoView = view;
+               self.showInfo = enabled;
+               window.plugins.nativeControls.createTabBarItem("info",label,"/www/img/icon-Tag.png", {onSelect: Menu.info}); 
+       };
+}
diff --git a/www/js/menuinterface.js b/www/js/menuinterface.js
new file mode 100644 (file)
index 0000000..596f859
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
+ * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+ */
+
+/**
+ *  
+ * @return Object literal singleton instance of MenuInterface
+ */
+var MenuInterface = { 
+       /**
+     * @param asset Path to the asset (relative to assets dir)
+     * @param target Path to DB file (relative to app db files dir)
+     * @param overwrite
+     * @param win Success callback
+     * @param fail Error callback
+     */
+       setInfoButton: function(label, enabled, win, fail) {
+               if (enabled == false) enabled = "false";
+               else enabled = "true";
+               return PhoneGap.exec(
+                       win, 
+                       fail, 
+                       "MenuInterface", 
+                       "setInfoButton", 
+                       [label, enabled]
+               );
+       },
+       setNightMode: function(enabled, win, fail) {
+               if (enabled == false) enabled = "false";
+               else enabled = "true";
+               return PhoneGap.exec(
+                       win, 
+                       fail, 
+                       "MenuInterface", 
+                       "setNightMode", 
+                       [enabled]
+               );
+       },
+};
+
diff --git a/www/js/phonegap-1.1.0.js b/www/js/phonegap-1.1.0.js
new file mode 100644 (file)
index 0000000..a18f29d
--- /dev/null
@@ -0,0 +1,4097 @@
+/*
+ * PhoneGap v1.1.0 is available under *either* the terms of the modified BSD license *or* the
+ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
+ * 
+ * Copyright (c) 2005-2010, Nitobi Software Inc.
+ * Copyright (c) 2010-2011, IBM Corporation
+ * Copyright (c) 2011, Codevise Solutions Ltd.
+ * Copyright (c) 2011, Proyectos Equis Ka, S.L.
+ * 
+ */
+
+if (typeof PhoneGap === "undefined") {
+
+if (typeof(DeviceInfo) !== 'object'){
+    DeviceInfo = {};
+}
+/**
+ * This represents the PhoneGap API itself, and provides a global namespace for accessing
+ * information about the state of PhoneGap.
+ * @class
+ */
+PhoneGap = {
+    // This queue holds the currently executing command and all pending
+    // commands executed with PhoneGap.exec().
+    commandQueue: [],
+    // Indicates if we're currently in the middle of flushing the command
+    // queue on the native side.
+    commandQueueFlushing: false,
+    _constructors: [],
+    documentEventHandler: {},   // Collection of custom document event handlers
+    windowEventHandler: {} 
+};
+
+/**
+ * List of resource files loaded by PhoneGap.
+ * This is used to ensure JS and other files are loaded only once.
+ */
+PhoneGap.resources = {base: true};
+
+/**
+ * Determine if resource has been loaded by PhoneGap
+ *
+ * @param name
+ * @return
+ */
+PhoneGap.hasResource = function(name) {
+    return PhoneGap.resources[name];
+};
+
+/**
+ * Add a resource to list of loaded resources by PhoneGap
+ *
+ * @param name
+ */
+PhoneGap.addResource = function(name) {
+    PhoneGap.resources[name] = true;
+};
+
+/**
+ * Boolean flag indicating if the PhoneGap API is available and initialized.
+ */ // TODO: Remove this, it is unused here ... -jm
+PhoneGap.available = DeviceInfo.uuid != undefined;
+
+/**
+ * Add an initialization function to a queue that ensures it will run and initialize
+ * application constructors only once PhoneGap has been initialized.
+ * @param {Function} func The function callback you want run once PhoneGap is initialized
+ */
+PhoneGap.addConstructor = function(func) {
+    var state = document.readyState;
+    if ( ( state == 'loaded' || state == 'complete' ) && DeviceInfo.uuid != null )
+    {
+        func();
+    }
+    else
+    {
+        PhoneGap._constructors.push(func);
+    }
+};
+
+(function() 
+ {
+    var timer = setInterval(function()
+    {
+                            
+        var state = document.readyState;
+                            
+        if ( ( state == 'loaded' || state == 'complete' ) && DeviceInfo.uuid != null )
+        {
+            clearInterval(timer); // stop looking
+            // run our constructors list
+            while (PhoneGap._constructors.length > 0) 
+            {
+                var constructor = PhoneGap._constructors.shift();
+                try 
+                {
+                    constructor();
+                } 
+                catch(e) 
+                {
+                    if (typeof(console['log']) == 'function')
+                    {
+                        console.log("Failed to run constructor: " + console.processMessage(e));
+                    }
+                    else
+                    {
+                        alert("Failed to run constructor: " + e.message);
+                    }
+                }
+            }
+            // all constructors run, now fire the deviceready event
+            var e = document.createEvent('Events'); 
+            e.initEvent('deviceready');
+            document.dispatchEvent(e);
+        }
+    }, 1);
+})();
+
+// session id for calls
+PhoneGap.sessionKey = 0;
+
+// centralized callbacks
+PhoneGap.callbackId = 0;
+PhoneGap.callbacks = {};
+PhoneGap.callbackStatus = {
+    NO_RESULT: 0,
+    OK: 1,
+    CLASS_NOT_FOUND_EXCEPTION: 2,
+    ILLEGAL_ACCESS_EXCEPTION: 3,
+    INSTANTIATION_EXCEPTION: 4,
+    MALFORMED_URL_EXCEPTION: 5,
+    IO_EXCEPTION: 6,
+    INVALID_ACTION: 7,
+    JSON_EXCEPTION: 8,
+    ERROR: 9
+    };
+
+/**
+ * Creates a gap bridge iframe used to notify the native code about queued
+ * commands.
+ *
+ * @private
+ */
+PhoneGap.createGapBridge = function() {
+    gapBridge = document.createElement("iframe");
+    gapBridge.setAttribute("style", "display:none;");
+    gapBridge.setAttribute("height","0px");
+    gapBridge.setAttribute("width","0px");
+    gapBridge.setAttribute("frameborder","0");
+    document.documentElement.appendChild(gapBridge);
+    return gapBridge;
+}
+
+/** 
+ * Execute a PhoneGap command by queuing it and letting the native side know
+ * there are queued commands. The native side will then request all of the
+ * queued commands and execute them.
+ *
+ * Arguments may be in one of two formats:
+ *
+ * FORMAT ONE (preferable)
+ * The native side will call PhoneGap.callbackSuccess or
+ * PhoneGap.callbackError, depending upon the result of the action.
+ *
+ * @param {Function} success    The success callback
+ * @param {Function} fail       The fail callback
+ * @param {String} service      The name of the service to use
+ * @param {String} action       The name of the action to use
+ * @param {String[]} [args]     Zero or more arguments to pass to the method
+ *      
+ * FORMAT TWO
+ * @param {String} command    Command to be run in PhoneGap, e.g.
+ *                            "ClassName.method"
+ * @param {String[]} [args]   Zero or more arguments to pass to the method
+ *                            object parameters are passed as an array object
+ *                            [object1, object2] each object will be passed as
+ *                            JSON strings 
+ */
+PhoneGap.exec = function() { 
+    if (!PhoneGap.available) {
+        alert("ERROR: Attempting to call PhoneGap.exec()"
+              +" before 'deviceready'. Ignoring.");
+        return;
+    }
+
+    var successCallback, failCallback, service, action, actionArgs;
+    var callbackId = null;
+    if (typeof arguments[0] !== "string") {
+        // FORMAT ONE
+        successCallback = arguments[0];
+        failCallback = arguments[1];
+        service = arguments[2];
+        action = arguments[3];
+        actionArgs = arguments[4];
+
+        // Since we need to maintain backwards compatibility, we have to pass
+        // an invalid callbackId even if no callback was provided since plugins
+        // will be expecting it. The PhoneGap.exec() implementation allocates
+        // an invalid callbackId and passes it even if no callbacks were given.
+        callbackId = 'INVALID';
+    } else {
+        // FORMAT TWO
+        splitCommand = arguments[0].split(".");
+        action = splitCommand.pop();
+        service = splitCommand.join(".");
+        actionArgs = Array.prototype.splice.call(arguments, 1);
+    }
+    
+    // Start building the command object.
+    var command = {
+        className: service,
+        methodName: action,
+        arguments: []
+    };
+
+    // Register the callbacks and add the callbackId to the positional
+    // arguments if given.
+    if (successCallback || failCallback) {
+        callbackId = service + PhoneGap.callbackId++;
+        PhoneGap.callbacks[callbackId] = 
+            {success:successCallback, fail:failCallback};
+    }
+    if (callbackId != null) {
+        command.arguments.push(callbackId);
+    }
+
+    for (var i = 0; i < actionArgs.length; ++i) {
+        var arg = actionArgs[i];
+        if (arg == undefined || arg == null) {
+            continue;
+        } else if (typeof(arg) == 'object') {
+            command.options = arg;
+        } else {
+            command.arguments.push(arg);
+        }
+    }
+
+    // Stringify and queue the command. We stringify to command now to
+    // effectively clone the command arguments in case they are mutated before
+    // the command is executed.
+    PhoneGap.commandQueue.push(JSON.stringify(command));
+
+    // If the queue length is 1, then that means it was empty before we queued
+    // the given command, so let the native side know that we have some
+    // commands to execute, unless the queue is currently being flushed, in
+    // which case the command will be picked up without notification.
+    if (PhoneGap.commandQueue.length == 1 && !PhoneGap.commandQueueFlushing) {
+        if (!PhoneGap.gapBridge) {
+            PhoneGap.gapBridge = PhoneGap.createGapBridge();
+        }
+
+        PhoneGap.gapBridge.src = "gap://ready";
+    }
+}
+
+/**
+ * Called by native code to retrieve all queued commands and clear the queue.
+ */
+PhoneGap.getAndClearQueuedCommands = function() {
+  json = JSON.stringify(PhoneGap.commandQueue);
+  PhoneGap.commandQueue = [];
+  return json;
+}
+
+/**
+ * Called by native code when returning successful result from an action.
+ *
+ * @param callbackId
+ * @param args
+ *        args.status - PhoneGap.callbackStatus
+ *        args.message - return value
+ *        args.keepCallback - 0 to remove callback, 1 to keep callback in PhoneGap.callbacks[]
+ */
+PhoneGap.callbackSuccess = function(callbackId, args) {
+    if (PhoneGap.callbacks[callbackId]) {
+
+        // If result is to be sent to callback
+        if (args.status == PhoneGap.callbackStatus.OK) {
+            try {
+                if (PhoneGap.callbacks[callbackId].success) {
+                       PhoneGap.callbacks[callbackId].success(args.message);
+                }
+            }
+            catch (e) {
+                console.log("Error in success callback: "+callbackId+" = "+e);
+            }
+        }
+    
+        // Clear callback if not expecting any more results
+        if (!args.keepCallback) {
+            delete PhoneGap.callbacks[callbackId];
+        }
+    }
+};
+
+/**
+ * Called by native code when returning error result from an action.
+ *
+ * @param callbackId
+ * @param args
+ */
+PhoneGap.callbackError = function(callbackId, args) {
+    if (PhoneGap.callbacks[callbackId]) {
+        try {
+            if (PhoneGap.callbacks[callbackId].fail) {
+                PhoneGap.callbacks[callbackId].fail(args.message);
+            }
+        }
+        catch (e) {
+            console.log("Error in error callback: "+callbackId+" = "+e);
+        }
+        
+        // Clear callback if not expecting any more results
+        if (!args.keepCallback) {
+            delete PhoneGap.callbacks[callbackId];
+        }
+    }
+};
+
+
+/**
+ * Does a deep clone of the object.
+ *
+ * @param obj
+ * @return
+ */
+PhoneGap.clone = function(obj) {
+    if(!obj) { 
+        return obj;
+    }
+
+    if(obj instanceof Array){
+        var retVal = new Array();
+        for(var i = 0; i < obj.length; ++i){
+            retVal.push(PhoneGap.clone(obj[i]));
+        }
+        return retVal;
+    }
+
+    if (obj instanceof Function) {
+        return obj;
+    }
+
+    if(!(obj instanceof Object)){
+        return obj;
+    }
+    
+    if (obj instanceof Date) {
+        return obj;
+    }
+
+    retVal = new Object();
+    for(i in obj){
+        if(!(i in retVal) || retVal[i] != obj[i]) {
+            retVal[i] = PhoneGap.clone(obj[i]);
+        }
+    }
+    return retVal;
+};
+
+// Intercept calls to document.addEventListener 
+PhoneGap.m_document_addEventListener = document.addEventListener;
+
+// Intercept calls to window.addEventListener
+PhoneGap.m_window_addEventListener = window.addEventListener;
+
+/**
+ * Add a custom window event handler.
+ *
+ * @param {String} event            The event name that callback handles
+ * @param {Function} callback       The event handler
+ */
+PhoneGap.addWindowEventHandler = function(event, callback) {
+    PhoneGap.windowEventHandler[event] = callback;
+}
+
+/**
+ * Add a custom document event handler.
+ *
+ * @param {String} event            The event name that callback handles
+ * @param {Function} callback       The event handler
+ */
+PhoneGap.addDocumentEventHandler = function(event, callback) {
+    PhoneGap.documentEventHandler[event] = callback;
+}
+
+/**
+ * Intercept adding document event listeners and handle our own
+ *
+ * @param {Object} evt
+ * @param {Function} handler
+ * @param capture
+ */
+document.addEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+           
+    // If subscribing to an event that is handled by a plugin
+    if (typeof PhoneGap.documentEventHandler[e] !== "undefined") {
+        if (PhoneGap.documentEventHandler[e](e, handler, true)) {
+            return; // Stop default behavior
+        }
+    }
+    
+    PhoneGap.m_document_addEventListener.call(document, evt, handler, capture); 
+};
+
+/**
+ * Intercept adding window event listeners and handle our own
+ *
+ * @param {Object} evt
+ * @param {Function} handler
+ * @param capture
+ */
+window.addEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+        
+    // If subscribing to an event that is handled by a plugin
+    if (typeof PhoneGap.windowEventHandler[e] !== "undefined") {
+        if (PhoneGap.windowEventHandler[e](e, handler, true)) {
+            return; // Stop default behavior
+        }
+    }
+        
+    PhoneGap.m_window_addEventListener.call(window, evt, handler, capture);
+};
+
+// Intercept calls to document.removeEventListener and watch for events that
+// are generated by PhoneGap native code
+PhoneGap.m_document_removeEventListener = document.removeEventListener;
+
+// Intercept calls to window.removeEventListener
+PhoneGap.m_window_removeEventListener = window.removeEventListener;
+
+/**
+ * Intercept removing document event listeners and handle our own
+ *
+ * @param {Object} evt
+ * @param {Function} handler
+ * @param capture
+ */
+document.removeEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+
+    // If unsubcribing from an event that is handled by a plugin
+    if (typeof PhoneGap.documentEventHandler[e] !== "undefined") {
+        if (PhoneGap.documentEventHandler[e](e, handler, false)) {
+            return; // Stop default behavior
+        }
+    }
+
+    PhoneGap.m_document_removeEventListener.call(document, evt, handler, capture);
+};
+
+/**
+ * Intercept removing window event listeners and handle our own
+ *
+ * @param {Object} evt
+ * @param {Function} handler
+ * @param capture
+ */
+window.removeEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+
+    // If unsubcribing from an event that is handled by a plugin
+    if (typeof PhoneGap.windowEventHandler[e] !== "undefined") {
+        if (PhoneGap.windowEventHandler[e](e, handler, false)) {
+            return; // Stop default behavior
+        }
+    }
+
+    PhoneGap.m_window_removeEventListener.call(window, evt, handler, capture);
+};
+
+/**
+ * Method to fire document event
+ *
+ * @param {String} type             The event type to fire
+ * @param {Object} data             Data to send with event
+ */
+PhoneGap.fireDocumentEvent = function(type, data) {
+    var e = document.createEvent('Events');
+    e.initEvent(type);
+    if (data) {
+        for (var i in data) {
+            e[i] = data[i];
+        }
+    }
+    document.dispatchEvent(e);
+};
+
+/**
+ * Method to fire window event
+ *
+ * @param {String} type             The event type to fire
+ * @param {Object} data             Data to send with event
+ */
+PhoneGap.fireWindowEvent = function(type, data) {
+    var e = document.createEvent('Events');
+    e.initEvent(type);
+    if (data) {
+        for (var i in data) {
+            e[i] = data[i];
+        }
+    }
+    window.dispatchEvent(e);
+};
+
+/**
+ * Method to fire event from native code
+ * Leaving this generic version to handle problems with iOS 3.x. Is currently used by orientation and battery events
+ * Remove when iOS 3.x no longer supported and call fireWindowEvent or fireDocumentEvent directly
+ */
+PhoneGap.fireEvent = function(type, target, data) {
+    var e = document.createEvent('Events');
+    e.initEvent(type);
+    if (data) {
+        for (var i in data) {
+            e[i] = data[i];
+        }
+    }
+    target = target || document;
+    if (target.dispatchEvent === undefined) { // ie window.dispatchEvent is undefined in iOS 3.x
+        target = document;
+    } 
+
+    target.dispatchEvent(e);
+};
+/**
+ * Create a UUID
+ *
+ * @return
+ */
+PhoneGap.createUUID = function() {
+    return PhoneGap.UUIDcreatePart(4) + '-' +
+        PhoneGap.UUIDcreatePart(2) + '-' +
+        PhoneGap.UUIDcreatePart(2) + '-' +
+        PhoneGap.UUIDcreatePart(2) + '-' +
+        PhoneGap.UUIDcreatePart(6);
+};
+
+PhoneGap.UUIDcreatePart = function(length) {
+    var uuidpart = "";
+    for (var i=0; i<length; i++) {
+        var uuidchar = parseInt((Math.random() * 256)).toString(16);
+        if (uuidchar.length == 1) {
+            uuidchar = "0" + uuidchar;
+        }
+        uuidpart += uuidchar;
+    }
+    return uuidpart;
+};
+};
+
+
+if (!PhoneGap.hasResource("debugconsole")) {
+       PhoneGap.addResource("debugconsole");
+       
+/**
+ * This class provides access to the debugging console.
+ * @constructor
+ */
+var DebugConsole = function() {
+    this.winConsole = window.console;
+    this.logLevel = DebugConsole.INFO_LEVEL;
+}
+
+// from most verbose, to least verbose
+DebugConsole.ALL_LEVEL    = 1; // same as first level
+DebugConsole.INFO_LEVEL   = 1;
+DebugConsole.WARN_LEVEL   = 2;
+DebugConsole.ERROR_LEVEL  = 4;
+DebugConsole.NONE_LEVEL   = 8;
+                                                                                                       
+DebugConsole.prototype.setLevel = function(level) {
+    this.logLevel = level;
+};
+
+/**
+ * Utility function for rendering and indenting strings, or serializing
+ * objects to a string capable of being printed to the console.
+ * @param {Object|String} message The string or object to convert to an indented string
+ * @private
+ */
+DebugConsole.prototype.processMessage = function(message, maxDepth) {
+       if (maxDepth === undefined) maxDepth = 0;
+    if (typeof(message) != 'object') {
+        return (this.isDeprecated ? "WARNING: debug object is deprecated, please use console object \n" + message : message);
+    } else {
+        /**
+         * @function
+         * @ignore
+         */
+        function indent(str) {
+            return str.replace(/^/mg, "    ");
+        }
+        /**
+         * @function
+         * @ignore
+         */
+        function makeStructured(obj, depth) {
+            var str = "";
+            for (var i in obj) {
+                try {
+                    if (typeof(obj[i]) == 'object' && depth < maxDepth) {
+                        str += i + ":\n" + indent(makeStructured(obj[i])) + "\n";
+                    } else {
+                        str += i + " = " + indent(String(obj[i])).replace(/^    /, "") + "\n";
+                    }
+                } catch(e) {
+                    str += i + " = EXCEPTION: " + e.message + "\n";
+                }
+            }
+            return str;
+        }
+        
+        return ("Object:\n" + makeStructured(message, maxDepth));
+    }
+};
+
+/**
+ * Print a normal log message to the console
+ * @param {Object|String} message Message or object to print to the console
+ */
+DebugConsole.prototype.log = function(message, maxDepth) {
+    if (PhoneGap.available && this.logLevel <= DebugConsole.INFO_LEVEL)
+        PhoneGap.exec(null, null, 'com.phonegap.debugconsole', 'log',
+            [ this.processMessage(message, maxDepth), { logLevel: 'INFO' } ]
+        );
+    else
+        this.winConsole.log(message);
+};
+
+/**
+ * Print a warning message to the console
+ * @param {Object|String} message Message or object to print to the console
+ */
+DebugConsole.prototype.warn = function(message, maxDepth) {
+    if (PhoneGap.available && this.logLevel <= DebugConsole.WARN_LEVEL)
+       PhoneGap.exec(null, null, 'com.phonegap.debugconsole', 'log',
+            [ this.processMessage(message, maxDepth), { logLevel: 'WARN' } ]
+        );
+    else
+        this.winConsole.error(message);
+};
+
+/**
+ * Print an error message to the console
+ * @param {Object|String} message Message or object to print to the console
+ */
+DebugConsole.prototype.error = function(message, maxDepth) {
+    if (PhoneGap.available && this.logLevel <= DebugConsole.ERROR_LEVEL)
+               PhoneGap.exec(null, null, 'com.phonegap.debugconsole', 'log',
+            [ this.processMessage(message, maxDepth), { logLevel: 'ERROR' } ]
+        );
+    else
+        this.winConsole.error(message);
+};
+
+PhoneGap.addConstructor(function() {
+    window.console = new DebugConsole();
+});
+};
+if (!PhoneGap.hasResource("position")) {
+       PhoneGap.addResource("position");
+
+/**
+ * This class contains position information.
+ * @param {Object} lat
+ * @param {Object} lng
+ * @param {Object} acc
+ * @param {Object} alt
+ * @param {Object} altAcc
+ * @param {Object} head
+ * @param {Object} vel
+ * @constructor
+ */
+Position = function(coords, timestamp) {
+       this.coords = Coordinates.cloneFrom(coords);
+    this.timestamp = timestamp || new Date().getTime();
+};
+
+Position.prototype.equals = function(other) {
+    return (this.coords && other && other.coords &&
+            this.coords.latitude == other.coords.latitude &&
+            this.coords.longitude == other.coords.longitude);
+};
+
+Position.prototype.clone = function()
+{
+    return new Position(
+        this.coords? this.coords.clone() : null,
+        this.timestamp? this.timestamp : new Date().getTime()
+    );
+}
+
+Coordinates = function(lat, lng, alt, acc, head, vel, altAcc) {
+       /**
+        * The latitude of the position.
+        */
+       this.latitude = lat;
+       /**
+        * The longitude of the position,
+        */
+       this.longitude = lng;
+       /**
+        * The altitude of the position.
+        */
+       this.altitude = alt;
+       /**
+        * The accuracy of the position.
+        */
+       this.accuracy = acc;
+       /**
+        * The direction the device is moving at the position.
+        */
+       this.heading = head;
+       /**
+        * The velocity with which the device is moving at the position.
+        */
+       this.speed = vel;
+       /**
+        * The altitude accuracy of the position.
+        */
+       this.altitudeAccuracy = (altAcc != 'undefined') ? altAcc : null; 
+};
+
+Coordinates.prototype.clone = function()
+{
+    return new Coordinates(
+        this.latitude,
+        this.longitude,
+        this.altitude,
+        this.accuracy,
+        this.heading,
+        this.speed,
+        this.altitudeAccuracy
+    );
+};
+
+Coordinates.cloneFrom = function(obj)
+{
+    return new Coordinates(
+        obj.latitude,
+        obj.longitude,
+        obj.altitude,
+        obj.accuracy,
+        obj.heading,
+        obj.speed,
+        obj.altitudeAccuracy
+    );
+};
+
+/**
+ * This class specifies the options for requesting position data.
+ * @constructor
+ */
+PositionOptions = function(enableHighAccuracy, timeout, maximumAge) {
+       /**
+        * Specifies the desired position accuracy.
+        */
+       this.enableHighAccuracy = enableHighAccuracy || false;
+       /**
+        * The timeout after which if position data cannot be obtained the errorCallback
+        * is called.
+        */
+       this.timeout = timeout || 10000;
+       /**
+     * The age of a cached position whose age is no greater than the specified time 
+     * in milliseconds. 
+     */
+       this.maximumAge = maximumAge || 0;
+       
+       if (this.maximumAge < 0) {
+               this.maximumAge = 0;
+       }
+};
+
+/**
+ * This class contains information about any GPS errors.
+ * @constructor
+ */
+PositionError = function(code, message) {
+       this.code = code || 0;
+       this.message = message || "";
+};
+
+PositionError.UNKNOWN_ERROR = 0;
+PositionError.PERMISSION_DENIED = 1;
+PositionError.POSITION_UNAVAILABLE = 2;
+PositionError.TIMEOUT = 3;
+
+};if (!PhoneGap.hasResource("acceleration")) {
+       PhoneGap.addResource("acceleration");
+       
+
+/**
+ * This class contains acceleration information
+ * @constructor
+ * @param {Number} x The force applied by the device in the x-axis.
+ * @param {Number} y The force applied by the device in the y-axis.
+ * @param {Number} z The force applied by the device in the z-axis.
+ */
+Acceleration = function(x, y, z) {
+       /**
+        * The force applied by the device in the x-axis.
+        */
+       this.x = x;
+       /**
+        * The force applied by the device in the y-axis.
+        */
+       this.y = y;
+       /**
+        * The force applied by the device in the z-axis.
+        */
+       this.z = z;
+       /**
+        * The time that the acceleration was obtained.
+        */
+       this.timestamp = new Date().getTime();
+}
+
+/**
+ * This class specifies the options for requesting acceleration data.
+ * @constructor
+ */
+AccelerationOptions = function() {
+       /**
+        * The timeout after which if acceleration data cannot be obtained the errorCallback
+        * is called.
+        */
+       this.timeout = 10000;
+}
+};if (!PhoneGap.hasResource("accelerometer")) {
+       PhoneGap.addResource("accelerometer");
+
+/**
+ * This class provides access to device accelerometer data.
+ * @constructor
+ */
+Accelerometer = function() 
+{
+       /**
+        * The last known acceleration.
+        */
+       this.lastAcceleration = new Acceleration(0,0,0);
+}
+
+/**
+ * Asynchronously aquires the current acceleration.
+ * @param {Function} successCallback The function to call when the acceleration
+ * data is available
+ * @param {Function} errorCallback The function to call when there is an error 
+ * getting the acceleration data.
+ * @param {AccelerationOptions} options The options for getting the accelerometer data
+ * such as timeout.
+ */
+Accelerometer.prototype.getCurrentAcceleration = function(successCallback, errorCallback, options) {
+       // If the acceleration is available then call success
+       // If the acceleration is not available then call error
+       
+       // Created for iPhone, Iphone passes back _accel obj litteral
+       if (typeof successCallback == "function") {
+               successCallback(this.lastAcceleration);
+       }
+};
+
+// private callback called from Obj-C by name
+Accelerometer.prototype._onAccelUpdate = function(x,y,z)
+{
+   this.lastAcceleration = new Acceleration(x,y,z);
+};
+
+/**
+ * Asynchronously aquires the acceleration repeatedly at a given interval.
+ * @param {Function} successCallback The function to call each time the acceleration
+ * data is available
+ * @param {Function} errorCallback The function to call when there is an error 
+ * getting the acceleration data.
+ * @param {AccelerationOptions} options The options for getting the accelerometer data
+ * such as timeout.
+ */
+
+Accelerometer.prototype.watchAcceleration = function(successCallback, errorCallback, options) {
+       //this.getCurrentAcceleration(successCallback, errorCallback, options);
+       // TODO: add the interval id to a list so we can clear all watches
+       var frequency = (options != undefined && options.frequency != undefined) ? options.frequency : 10000;
+       var updatedOptions = {
+               desiredFrequency:frequency 
+       }
+       PhoneGap.exec(null, null, "com.phonegap.accelerometer", "start", [options]);
+
+       return setInterval(function() {
+               navigator.accelerometer.getCurrentAcceleration(successCallback, errorCallback, options);
+       }, frequency);
+};
+
+/**
+ * Clears the specified accelerometer watch.
+ * @param {String} watchId The ID of the watch returned from #watchAcceleration.
+ */
+Accelerometer.prototype.clearWatch = function(watchId) {
+       PhoneGap.exec(null, null, "com.phonegap.accelerometer", "stop", []);
+       clearInterval(watchId);
+};
+
+Accelerometer.install = function()
+{
+    if (typeof navigator.accelerometer == "undefined") {
+               navigator.accelerometer = new Accelerometer();
+       }
+};
+
+Accelerometer.installDeviceMotionHandler = function()
+{
+       if (!(window.DeviceMotionEvent == undefined)) {
+               // supported natively, so we don't have to add support
+               return;
+       }       
+       
+       var self = this;
+       var devicemotionEvent = 'devicemotion';
+       self.deviceMotionWatchId = null;
+       self.deviceMotionListenerCount = 0;
+       self.deviceMotionLastEventTimestamp = 0;
+       
+       // backup original `window.addEventListener`, `window.removeEventListener`
+    var _addEventListener = window.addEventListener;
+    var _removeEventListener = window.removeEventListener;
+                                                                                                       
+       var windowDispatchAvailable = !(window.dispatchEvent === undefined); // undefined in iOS 3.x
+                                                                                                       
+       var accelWin = function(acceleration) {
+               var evt = document.createEvent('Events');
+           evt.initEvent(devicemotionEvent);
+       
+               evt.acceleration = null; // not all devices have gyroscope, don't care for now if we actually have it.
+               evt.rotationRate = null; // not all devices have gyroscope, don't care for now if we actually have it:
+               evt.accelerationIncludingGravity = acceleration; // accelerometer, all iOS devices have it
+               
+               var currentTime = new Date().getTime();
+               evt.interval =  (self.deviceMotionLastEventTimestamp == 0) ? 0 : (currentTime - self.deviceMotionLastEventTimestamp);
+               self.deviceMotionLastEventTimestamp = currentTime;
+               
+               if (windowDispatchAvailable) {
+                       window.dispatchEvent(evt);
+               } else {
+                       document.dispatchEvent(evt);
+               }
+       };
+       
+       var accelFail = function() {
+               
+       };
+                                                                                                       
+    // override `window.addEventListener`
+    window.addEventListener = function() {
+        if (arguments[0] === devicemotionEvent) {
+            ++(self.deviceMotionListenerCount);
+                       if (self.deviceMotionListenerCount == 1) { // start
+                               self.deviceMotionWatchId = navigator.accelerometer.watchAcceleration(accelWin, accelFail, { frequency:500});
+                       }
+               } 
+                                                                                                       
+               if (!windowDispatchAvailable) {
+                       return document.addEventListener.apply(this, arguments);
+               } else {
+                       return _addEventListener.apply(this, arguments);
+               }
+    }; 
+
+    // override `window.removeEventListener'
+    window.removeEventListener = function() {
+        if (arguments[0] === devicemotionEvent) {
+            --(self.deviceMotionListenerCount);
+                       if (self.deviceMotionListenerCount == 0) { // stop
+                               navigator.accelerometer.clearWatch(self.deviceMotionWatchId);
+                       }
+               } 
+               
+               if (!windowDispatchAvailable) {
+                       return document.removeEventListener.apply(this, arguments);
+               } else {
+                       return _removeEventListener.apply(this, arguments);
+               }
+    }; 
+};
+
+
+PhoneGap.addConstructor(Accelerometer.install);
+PhoneGap.addConstructor(Accelerometer.installDeviceMotionHandler);
+
+};/*
+ * PhoneGap is available under *either* the terms of the modified BSD license *or* the
+ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
+ *
+ * Copyright (c) 2005-2010, Nitobi Software Inc.
+ * Copyright (c) 2010-2011, IBM Corporation
+ */
+
+if (!PhoneGap.hasResource("battery")) {
+PhoneGap.addResource("battery");
+
+/**
+ * This class contains information about the current battery status.
+ * @constructor
+ */
+var Battery = function() {
+    this._level = null;
+    this._isPlugged = null;
+    this._batteryListener = [];
+    this._lowListener = [];
+    this._criticalListener = [];
+};
+
+/**
+ * Registers as an event producer for battery events.
+ * 
+ * @param {Object} eventType
+ * @param {Object} handler
+ * @param {Object} add
+ */
+Battery.prototype.eventHandler = function(eventType, handler, add) {
+    var me = navigator.battery;
+    if (add) {
+        // If there are no current registered event listeners start the battery listener on native side.
+        if (me._batteryListener.length === 0 && me._lowListener.length === 0 && me._criticalListener.length === 0) {
+            PhoneGap.exec(me._status, me._error, "com.phonegap.battery", "start", []);
+        }
+        
+        // Register the event listener in the proper array
+        if (eventType === "batterystatus") {
+            var pos = me._batteryListener.indexOf(handler);
+            if (pos === -1) {
+               me._batteryListener.push(handler);
+            }
+        } else if (eventType === "batterylow") {
+            var pos = me._lowListener.indexOf(handler);
+            if (pos === -1) {
+               me._lowListener.push(handler);
+            }
+        } else if (eventType === "batterycritical") {
+            var pos = me._criticalListener.indexOf(handler);
+            if (pos === -1) {
+               me._criticalListener.push(handler);
+            }
+        }
+    } else {
+        // Remove the event listener from the proper array
+        if (eventType === "batterystatus") {
+            var pos = me._batteryListener.indexOf(handler);
+            if (pos > -1) {
+                me._batteryListener.splice(pos, 1);        
+            }
+        } else if (eventType === "batterylow") {
+            var pos = me._lowListener.indexOf(handler);
+            if (pos > -1) {
+                me._lowListener.splice(pos, 1);        
+            }
+        } else if (eventType === "batterycritical") {
+            var pos = me._criticalListener.indexOf(handler);
+            if (pos > -1) {
+                me._criticalListener.splice(pos, 1);        
+            }
+        }
+        
+        // If there are no more registered event listeners stop the battery listener on native side.
+        if (me._batteryListener.length === 0 && me._lowListener.length === 0 && me._criticalListener.length === 0) {
+            PhoneGap.exec(null, null, "com.phonegap.battery", "stop", []);
+        }
+    }
+};
+
+/**
+ * Callback for battery status
+ * 
+ * @param {Object} info                        keys: level, isPlugged
+ */
+Battery.prototype._status = function(info) {
+       if (info) {
+               var me = this;
+               if (me._level != info.level || me._isPlugged != info.isPlugged) {
+                       // Fire batterystatus event
+                       //PhoneGap.fireWindowEvent("batterystatus", info);
+                       // use this workaround since iOS 3.x does have window.dispatchEvent
+                       PhoneGap.fireEvent("batterystatus", window, info);      
+
+                       // Fire low battery event
+                       if (info.level == 20 || info.level == 5) {
+                               if (info.level == 20) {
+                                       //PhoneGap.fireWindowEvent("batterylow", info);
+                                       // use this workaround since iOS 3.x does not have window.dispatchEvent
+                                       PhoneGap.fireEvent("batterylow", window, info);
+                               }
+                               else {
+                                       //PhoneGap.fireWindowEvent("batterycritical", info);
+                                       // use this workaround since iOS 3.x does not have window.dispatchEvent
+                                       PhoneGap.fireEvent("batterycritical", window, info);
+                               }
+                       }
+               }
+               me._level = info.level;
+               me._isPlugged = info.isPlugged; 
+       }
+};
+
+/**
+ * Error callback for battery start
+ */
+Battery.prototype._error = function(e) {
+    console.log("Error initializing Battery: " + e);
+};
+
+PhoneGap.addConstructor(function() {
+    if (typeof navigator.battery === "undefined") {
+        navigator.battery = new Battery();
+        PhoneGap.addWindowEventHandler("batterystatus", navigator.battery.eventHandler);
+        PhoneGap.addWindowEventHandler("batterylow", navigator.battery.eventHandler);
+        PhoneGap.addWindowEventHandler("batterycritical", navigator.battery.eventHandler);
+    }
+});
+}if (!PhoneGap.hasResource("camera")) {
+       PhoneGap.addResource("camera");
+       
+
+/**
+ * This class provides access to the device camera.
+ * @constructor
+ */
+Camera = function() {
+       
+}
+/**
+ *  Available Camera Options
+ *  {boolean} allowEdit - true to allow editing image, default = false
+ *     {number} quality 0-100 (low to high) default =  100
+ *  {Camera.DestinationType} destinationType default = DATA_URL
+ *     {Camera.PictureSourceType} sourceType default = CAMERA
+ *     {number} targetWidth - width in pixels to scale image default = 0 (no scaling)
+ *  {number} targetHeight - height in pixels to scale image default = 0 (no scaling)
+ *  {Camera.EncodingType} - encodingType default = JPEG
+ */
+/**
+ * Format of image that is returned from getPicture.
+ *
+ * Example: navigator.camera.getPicture(success, fail,
+ *              { quality: 80,
+ *                destinationType: Camera.DestinationType.DATA_URL,
+ *                sourceType: Camera.PictureSourceType.PHOTOLIBRARY})
+ */
+Camera.DestinationType = {
+    DATA_URL: 0,                // Return base64 encoded string
+    FILE_URI: 1                 // Return file uri 
+};
+Camera.prototype.DestinationType = Camera.DestinationType;
+
+/**
+ * Source to getPicture from.
+ *
+ * Example: navigator.camera.getPicture(success, fail,
+ *              { quality: 80,
+ *                destinationType: Camera.DestinationType.DATA_URL,
+ *                sourceType: Camera.PictureSourceType.PHOTOLIBRARY})
+ */
+Camera.PictureSourceType = {
+    PHOTOLIBRARY : 0,           // Choose image from picture library 
+    CAMERA : 1,                 // Take picture from camera
+    SAVEDPHOTOALBUM : 2         // Choose image from picture library 
+};
+Camera.prototype.PictureSourceType = Camera.PictureSourceType;
+
+/** 
+ * Encoding of image returned from getPicture. 
+ * 
+ * Example: navigator.camera.getPicture(success, fail, 
+ *              { quality: 80, 
+ *                destinationType: Camera.DestinationType.DATA_URL, 
+ *                sourceType: Camera.PictureSourceType.CAMERA, 
+ *                encodingType: Camera.EncodingType.PNG}) 
+ */ 
+Camera.EncodingType = { 
+       JPEG: 0,                    // Return JPEG encoded image 
+       PNG: 1                      // Return PNG encoded image 
+};
+Camera.prototype.EncodingType = Camera.EncodingType;
+
+/** 
+ * Type of pictures to select from.  Only applicable when
+ *     PictureSourceType is PHOTOLIBRARY or SAVEDPHOTOALBUM 
+ * 
+ * Example: navigator.camera.getPicture(success, fail, 
+ *              { quality: 80, 
+ *                destinationType: Camera.DestinationType.DATA_URL, 
+ *                sourceType: Camera.PictureSourceType.PHOTOLIBRARY, 
+ *                mediaType: Camera.MediaType.PICTURE}) 
+ */ 
+Camera.MediaType = { 
+       PICTURE: 0,             // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType
+       VIDEO: 1,                // allow selection of video only, ONLY RETURNS URL
+       ALLMEDIA : 2                    // allow selection from all media types
+};
+Camera.prototype.MediaType = Camera.MediaType;
+
+/**
+ * Gets a picture from source defined by "options.sourceType", and returns the
+ * image as defined by the "options.destinationType" option.
+
+ * The defaults are sourceType=CAMERA and destinationType=DATA_URL.
+ *
+ * @param {Function} successCallback
+ * @param {Function} errorCallback
+ * @param {Object} options
+ */
+Camera.prototype.getPicture = function(successCallback, errorCallback, options) {
+       // successCallback required
+       if (typeof successCallback != "function") {
+        console.log("Camera Error: successCallback is not a function");
+        return;
+    }
+
+    // errorCallback optional
+    if (errorCallback && (typeof errorCallback != "function")) {
+        console.log("Camera Error: errorCallback is not a function");
+        return;
+    }
+       
+       PhoneGap.exec(successCallback, errorCallback, "com.phonegap.camera","getPicture",[options]);
+};
+
+
+
+PhoneGap.addConstructor(function() {
+    if (typeof navigator.camera == "undefined") navigator.camera = new Camera();
+});
+};
+
+if (!PhoneGap.hasResource("device")) {
+       PhoneGap.addResource("device");
+
+/**
+ * this represents the mobile device, and provides properties for inspecting the model, version, UUID of the
+ * phone, etc.
+ * @constructor
+ */
+Device = function() 
+{
+    this.platform = null;
+    this.version  = null;
+    this.name     = null;
+    this.phonegap      = null;
+    this.uuid     = null;
+    try 
+       {      
+               this.platform = DeviceInfo.platform;
+               this.version  = DeviceInfo.version;
+               this.name     = DeviceInfo.name;
+               this.phonegap = DeviceInfo.gap;
+               this.uuid     = DeviceInfo.uuid;
+
+    } 
+       catch(e) 
+       {
+        // TODO: 
+    }
+       this.available = PhoneGap.available = this.uuid != null;
+}
+
+PhoneGap.addConstructor(function() {
+       if (typeof navigator.device === "undefined") {
+       navigator.device = window.device = new Device();
+       }
+});
+};
+
+if (!PhoneGap.hasResource("capture")) {
+       PhoneGap.addResource("capture");
+/**
+ * The CaptureError interface encapsulates all errors in the Capture API.
+ */
+function CaptureError() {
+   this.code = null;
+};
+
+// Capture error codes
+CaptureError.CAPTURE_INTERNAL_ERR = 0;
+CaptureError.CAPTURE_APPLICATION_BUSY = 1;
+CaptureError.CAPTURE_INVALID_ARGUMENT = 2;
+CaptureError.CAPTURE_NO_MEDIA_FILES = 3;
+CaptureError.CAPTURE_NOT_SUPPORTED = 20;
+
+/**
+ * The Capture interface exposes an interface to the camera and microphone of the hosting device.
+ */
+function Capture() {
+       this.supportedAudioModes = [];
+       this.supportedImageModes = [];
+       this.supportedVideoModes = [];
+};
+
+/**
+ * Launch audio recorder application for recording audio clip(s).
+ * 
+ * @param {Function} successCB
+ * @param {Function} errorCB
+ * @param {CaptureAudioOptions} options
+ *
+ * No audio recorder to launch for iOS - return CAPTURE_NOT_SUPPORTED
+ */
+Capture.prototype.captureAudio = function(successCallback, errorCallback, options) {
+       /*if (errorCallback && typeof errorCallback === "function") {
+               errorCallback({
+                               "code": CaptureError.CAPTURE_NOT_SUPPORTED
+                       });
+       }*/
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.mediacapture", "captureAudio", [options]);
+};
+
+/**
+ * Launch camera application for taking image(s).
+ * 
+ * @param {Function} successCB
+ * @param {Function} errorCB
+ * @param {CaptureImageOptions} options
+ */
+Capture.prototype.captureImage = function(successCallback, errorCallback, options) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.mediacapture", "captureImage", [options]);
+};
+
+/**
+ * Launch camera application for taking image(s).
+ * 
+ * @param {Function} successCB
+ * @param {Function} errorCB
+ * @param {CaptureImageOptions} options
+ */
+Capture.prototype._castMediaFile = function(pluginResult) {
+    var mediaFiles = [];
+    var i;
+    for (i=0; i<pluginResult.message.length; i++) {
+        var mediaFile = new MediaFile();
+           mediaFile.name = pluginResult.message[i].name;
+           mediaFile.fullPath = pluginResult.message[i].fullPath;
+           mediaFile.type = pluginResult.message[i].type;
+           mediaFile.lastModifiedDate = pluginResult.message[i].lastModifiedDate;
+           mediaFile.size = pluginResult.message[i].size;
+        mediaFiles.push(mediaFile);
+    }
+    pluginResult.message = mediaFiles;
+    return pluginResult;
+};
+
+/**
+ * Launch device camera application for recording video(s).
+ * 
+ * @param {Function} successCB
+ * @param {Function} errorCB
+ * @param {CaptureVideoOptions} options
+ */
+Capture.prototype.captureVideo = function(successCallback, errorCallback, options) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.mediacapture", "captureVideo", [options]);
+};
+
+/**
+ * Encapsulates a set of parameters that the capture device supports.
+ */
+function ConfigurationData() {
+    // The ASCII-encoded string in lower case representing the media type. 
+    this.type; 
+    // The height attribute represents height of the image or video in pixels. 
+    // In the case of a sound clip this attribute has value 0. 
+    this.height = 0;
+    // The width attribute represents width of the image or video in pixels. 
+    // In the case of a sound clip this attribute has value 0
+    this.width = 0;
+};
+
+/**
+ * Encapsulates all image capture operation configuration options.
+ */
+var CaptureImageOptions = function() {
+    // Upper limit of images user can take. Value must be equal or greater than 1.
+    this.limit = 1; 
+    // The selected image mode. Must match with one of the elements in supportedImageModes array.
+    this.mode = null; 
+};
+
+/**
+ * Encapsulates all video capture operation configuration options.
+ */
+var CaptureVideoOptions = function() {
+    // Upper limit of videos user can record. Value must be equal or greater than 1.
+    this.limit = 1;
+    // Maximum duration of a single video clip in seconds.
+    this.duration = 0;
+    // The selected video mode. Must match with one of the elements in supportedVideoModes array.
+    this.mode = null;
+};
+
+/**
+ * Encapsulates all audio capture operation configuration options.
+ */
+var CaptureAudioOptions = function() {
+    // Upper limit of sound clips user can record. Value must be equal or greater than 1.
+    this.limit = 1;
+    // Maximum duration of a single sound clip in seconds.
+    this.duration = 0;
+    // The selected audio mode. Must match with one of the elements in supportedAudioModes array.
+    this.mode = null;
+};
+
+/**
+ * Represents a single file.
+ * 
+ * name {DOMString} name of the file, without path information
+ * fullPath {DOMString} the full path of the file, including the name
+ * type {DOMString} mime type
+ * lastModifiedDate {Date} last modified date
+ * size {Number} size of the file in bytes
+ */
+function MediaFile(name, fullPath, type, lastModifiedDate, size) {
+    this.name = name || null;
+    this.fullPath = fullPath || null;
+    this.type = type || null;
+    this.lastModifiedDate = lastModifiedDate || null;
+    this.size = size || 0;
+}
+
+/**
+ * Request capture format data for a specific file and type
+ * 
+ * @param {Function} successCB
+ * @param {Function} errorCB
+ */
+MediaFile.prototype.getFormatData = function(successCallback, errorCallback) {
+       if (typeof this.fullPath === "undefined" || this.fullPath === null) {
+               errorCallback({
+                               "code": CaptureError.CAPTURE_INVALID_ARGUMENT
+                       });
+       } else {
+       PhoneGap.exec(successCallback, errorCallback, "com.phonegap.mediacapture", "getFormatData", [this.fullPath, this.type]);
+       }       
+};
+
+/**
+ * MediaFileData encapsulates format information of a media file.
+ * 
+ * @param {DOMString} codecs
+ * @param {long} bitrate
+ * @param {long} height
+ * @param {long} width
+ * @param {float} duration
+ */
+function MediaFileData(codecs, bitrate, height, width, duration) {
+    this.codecs = codecs || null;
+    this.bitrate = bitrate || 0;
+    this.height = height || 0;
+    this.width = width || 0;
+    this.duration = duration || 0;
+}
+
+PhoneGap.addConstructor(function() {
+    if (typeof navigator.device === "undefined") {
+        navigator.device = window.device = new Device();
+    }
+    if (typeof navigator.device.capture === "undefined") {
+        navigator.device.capture = window.device.capture = new Capture();
+    }
+});
+};
+if (!PhoneGap.hasResource("contact")) {
+       PhoneGap.addResource("contact");
+
+
+/**
+* Contains information about a single contact.
+* @param {DOMString} id unique identifier
+* @param {DOMString} displayName
+* @param {ContactName} name
+* @param {DOMString} nickname
+* @param {ContactField[]} phoneNumbers array of phone numbers
+* @param {ContactField[]} emails array of email addresses
+* @param {ContactAddress[]} addresses array of addresses
+* @param {ContactField[]} ims instant messaging user ids
+* @param {ContactOrganization[]} organizations
+* @param {DOMString} birthday contact's birthday
+* @param {DOMString} note user notes about contact
+* @param {ContactField[]} photos
+* @param {Array.<ContactField>} categories
+* @param {ContactField[]} urls contact's web sites
+*/
+var Contact = function(id, displayName, name, nickname, phoneNumbers, emails, addresses,
+    ims, organizations, birthday, note, photos, categories, urls) {
+    this.id = id || null;
+    this.displayName = displayName || null;
+    this.name = name || null; // ContactName
+    this.nickname = nickname || null;
+    this.phoneNumbers = phoneNumbers || null; // ContactField[]
+    this.emails = emails || null; // ContactField[]
+    this.addresses = addresses || null; // ContactAddress[]
+    this.ims = ims || null; // ContactField[]
+    this.organizations = organizations || null; // ContactOrganization[]
+    this.birthday = birthday || null; // JS Date
+    this.note = note || null;
+    this.photos = photos || null; // ContactField[]
+    this.categories = categories || null; 
+    this.urls = urls || null; // ContactField[]
+};
+
+/**
+* Converts Dates to milliseconds before sending to iOS
+*/
+Contact.prototype.convertDatesOut = function()
+{
+       var dates = new Array("birthday");
+       for (var i=0; i<dates.length; i++){
+               var value = this[dates[i]];
+               if (value){
+                       if (!value instanceof Date){
+                               try {
+                                       value = new Date(value);
+                               } catch(exception){
+                                       value = null;
+                               }
+                       }
+                       if (value instanceof Date){
+                               value = value.valueOf();
+                       }
+                       this[dates[i]] = value;
+               }
+       }
+       
+};
+/**
+* Converts milliseconds to JS Date when returning from iOS
+*/
+Contact.prototype.convertDatesIn = function()
+{
+       var dates = new Array("birthday");
+       for (var i=0; i<dates.length; i++){
+               var value = this[dates[i]];
+               if (value){
+                       try {
+                               this[dates[i]] = new Date(parseFloat(value));
+                       } catch (exception){
+                               console.log("exception creating date");
+                       }
+               }
+       }
+};
+/**
+* Removes contact from device storage.
+* @param successCB success callback
+* @param errorCB error callback (optional)
+*/
+Contact.prototype.remove = function(successCB, errorCB) {
+       if (this.id == null) {
+        var errorObj = new ContactError();
+        errorObj.code = ContactError.UNKNOWN_ERROR;
+        errorCB(errorObj);
+    }
+    else {
+        PhoneGap.exec(successCB, errorCB, "com.phonegap.contacts", "remove", [{ "contact": this}]);
+    }
+};
+/**
+* iOS ONLY
+* displays contact via iOS UI
+*      NOT part of W3C spec so no official documentation
+*
+* @param errorCB error callback
+* @param options object
+*      allowsEditing: boolean AS STRING
+*              "true" to allow editing the contact
+*              "false" (default) display contact
+*/
+Contact.prototype.display = function(errorCB, options) { 
+       if (this.id == null) {
+        if (typeof errorCB == "function") {
+               var errorObj = new ContactError();
+               errorObj.code = ContactError.UNKNOWN_ERROR;
+               errorCB(errorObj);
+               }
+    }
+    else {
+        PhoneGap.exec(null, errorCB, "com.phonegap.contacts","displayContact", [this.id, options]);
+    }
+};
+
+/**
+* Creates a deep copy of this Contact.
+* With the contact ID set to null.
+* @return copy of this Contact
+*/
+Contact.prototype.clone = function() {
+    var clonedContact = PhoneGap.clone(this);
+    clonedContact.id = null;
+    // Loop through and clear out any id's in phones, emails, etc.
+    if (clonedContact.phoneNumbers) {
+       for (i=0; i<clonedContact.phoneNumbers.length; i++) {
+               clonedContact.phoneNumbers[i].id = null;
+       }
+    }
+    if (clonedContact.emails) {
+       for (i=0; i<clonedContact.emails.length; i++) {
+               clonedContact.emails[i].id = null;
+       }
+    }
+    if (clonedContact.addresses) {
+       for (i=0; i<clonedContact.addresses.length; i++) {
+               clonedContact.addresses[i].id = null;
+       }
+    }
+    if (clonedContact.ims) {
+       for (i=0; i<clonedContact.ims.length; i++) {
+               clonedContact.ims[i].id = null;
+       }
+    }
+    if (clonedContact.organizations) {
+       for (i=0; i<clonedContact.organizations.length; i++) {
+               clonedContact.organizations[i].id = null;
+       }
+    }
+    if (clonedContact.photos) {
+       for (i=0; i<clonedContact.photos.length; i++) {
+               clonedContact.photos[i].id = null;
+       }
+    }
+    if (clonedContact.urls) {
+       for (i=0; i<clonedContact.urls.length; i++) {
+               clonedContact.urls[i].id = null;
+       }
+    }
+    return clonedContact;
+};
+
+/**
+* Persists contact to device storage.
+* @param successCB success callback
+* @param errorCB error callback - optional
+*/
+Contact.prototype.save = function(successCB, errorCB) {
+       // don't modify the original contact
+       var cloned = PhoneGap.clone(this);
+       cloned.convertDatesOut(); 
+       PhoneGap.exec(successCB, errorCB, "com.phonegap.contacts","save", [{"contact": cloned}]);
+};
+
+/**
+* Contact name.
+* @param formatted
+* @param familyName
+* @param givenName
+* @param middle
+* @param prefix
+* @param suffix
+*/
+var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) {
+    this.formatted = formatted != "undefined" ? formatted : null;
+    this.familyName = familyName != "undefined" ? familyName : null;
+    this.givenName = givenName != "undefined" ? givenName : null;
+    this.middleName = middle != "undefined" ? middle : null;
+    this.honorificPrefix = prefix != "undefined" ? prefix : null;
+    this.honorificSuffix = suffix != "undefined" ? suffix : null;
+};
+
+/**
+* Generic contact field.
+* @param type
+* @param value
+* @param pref
+* @param id
+*/
+var ContactField = function(type, value, pref, id) {
+    this.type = type != "undefined" ? type : null;
+    this.value = value != "undefined" ? value : null;
+    this.pref = pref != "undefined" ? pref : null;
+    this.id = id != "undefined" ? id : null;
+};
+
+/**
+* Contact address.
+* @param pref - boolean is primary / preferred address
+* @param type - string - work, home…..
+* @param formatted
+* @param streetAddress
+* @param locality
+* @param region
+* @param postalCode
+* @param country
+*/
+var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country, id) {
+       this.pref = pref != "undefined" ? pref : null;
+       this.type = type != "undefined" ? type : null;
+    this.formatted = formatted != "undefined" ? formatted : null;
+    this.streetAddress = streetAddress != "undefined" ? streetAddress : null;
+    this.locality = locality != "undefined" ? locality : null;
+    this.region = region != "undefined" ? region : null;
+    this.postalCode = postalCode != "undefined" ? postalCode : null;
+    this.country = country != "undefined" ? country : null;
+    this.id = id != "undefined" ? id : null;
+};
+
+/**
+* Contact organization.
+* @param pref - boolean is primary / preferred address
+* @param type - string - work, home…..
+* @param name
+* @param dept
+* @param title
+*/
+var ContactOrganization = function(pref, type, name, dept, title) {
+       this.pref = pref != "undefined" ? pref : null;
+       this.type = type != "undefined" ? type : null;
+    this.name = name != "undefined" ? name : null;
+    this.department = dept != "undefined" ? dept : null;
+    this.title = title != "undefined" ? title : null;
+};
+
+/**
+* Contact account.
+* @param domain
+* @param username
+* @param userid
+*/
+/*var ContactAccount = function(domain, username, userid) {
+    this.domain = domain != "undefined" ? domain : null;
+    this.username = username != "undefined" ? username : null;
+    this.userid = userid != "undefined" ? userid : null;
+}*/
+
+/**
+* Represents a group of Contacts.
+*/
+var Contacts = function() {
+    this.inProgress = false;
+    this.records = new Array();
+};
+/**
+* Returns an array of Contacts matching the search criteria.
+* @param fields that should be searched
+* @param successCB success callback
+* @param errorCB error callback (optional)
+* @param {ContactFindOptions} options that can be applied to contact searching
+* @return array of Contacts matching search criteria
+*/
+Contacts.prototype.find = function(fields, successCB, errorCB, options) {
+       if (successCB === null) {
+        throw new TypeError("You must specify a success callback for the find command.");
+    }
+    if (fields === null || fields === "undefined" || fields.length === "undefined" || fields.length <= 0) {
+       if (typeof errorCB === "function") {
+                       errorCB({"code": ContactError.INVALID_ARGUMENT_ERROR});
+       }
+    } else {
+               PhoneGap.exec(successCB, errorCB, "com.phonegap.contacts","search", [{"fields":fields, "findOptions":options}]);
+    }
+};
+/**
+* need to turn the array of JSON strings representing contact objects into actual objects
+* @param array of JSON strings with contact data
+* @return call results callback with array of Contact objects
+*  This function is called from objective C Contacts.search() method.
+*/
+Contacts.prototype._findCallback = function(pluginResult) {
+       var contacts = new Array();
+       try {
+               for (var i=0; i<pluginResult.message.length; i++) {
+                       var newContact = navigator.contacts.create(pluginResult.message[i]); 
+                       newContact.convertDatesIn();
+                       contacts.push(newContact);
+               }
+               pluginResult.message = contacts;
+       } catch(e){
+                       console.log("Error parsing contacts: " +e);
+       }
+       return pluginResult;
+}
+
+/**
+* need to turn the JSON string representing contact object into actual object
+* @param JSON string with contact data
+* Call stored results function with  Contact object
+*  This function is called from objective C Contacts remove and save methods
+*/
+Contacts.prototype._contactCallback = function(pluginResult)
+{
+       var newContact = null;
+       if (pluginResult.message){
+               try {
+                       newContact = navigator.contacts.create(pluginResult.message);
+                       newContact.convertDatesIn();
+               } catch(e){
+                       console.log("Error parsing contact");
+               }
+       }
+       pluginResult.message = newContact;
+       return pluginResult;
+       
+};
+/** 
+* Need to return an error object rather than just a single error code
+* @param error code
+* Call optional error callback if found.
+* Called from objective c find, remove, and save methods on error.
+*/
+Contacts.prototype._errCallback = function(pluginResult)
+{
+       var errorObj = new ContactError();
+       errorObj.code = pluginResult.message;
+       pluginResult.message = errorObj;
+       return pluginResult;
+};
+// iPhone only api to create a new contact via the GUI
+Contacts.prototype.newContactUI = function(successCallback) { 
+    PhoneGap.exec(successCallback, null, "com.phonegap.contacts","newContact", []);
+};
+// iPhone only api to select a contact via the GUI
+Contacts.prototype.chooseContact = function(successCallback, options) {
+    PhoneGap.exec(successCallback, null, "com.phonegap.contacts","chooseContact", options);
+};
+
+
+/**
+* This function creates a new contact, but it does not persist the contact
+* to device storage. To persist the contact to device storage, invoke
+* contact.save().
+* @param properties an object who's properties will be examined to create a new Contact
+* @returns new Contact object
+*/
+Contacts.prototype.create = function(properties) {
+    var i;
+    var contact = new Contact();
+    for (i in properties) {
+        if (contact[i] !== 'undefined') {
+            contact[i] = properties[i];
+        }
+    }
+    return contact;
+};
+
+/**
+ * ContactFindOptions.
+ * @param filter used to match contacts against
+ * @param multiple boolean used to determine if more than one contact should be returned
+ */
+var ContactFindOptions = function(filter, multiple, updatedSince) {
+    this.filter = filter || '';
+    this.multiple = multiple || false;
+};
+
+/**
+ *  ContactError.
+ *  An error code assigned by an implementation when an error has occurred
+ */
+var ContactError = function() {
+    this.code=null;
+};
+
+/**
+ * Error codes
+ */
+ContactError.UNKNOWN_ERROR = 0;
+ContactError.INVALID_ARGUMENT_ERROR = 1;
+ContactError.TIMEOUT_ERROR = 2;
+ContactError.PENDING_OPERATION_ERROR = 3;
+ContactError.IO_ERROR = 4;
+ContactError.NOT_SUPPORTED_ERROR = 5;
+ContactError.PERMISSION_DENIED_ERROR = 20;
+
+/**
+ * Add the contact interface into the browser.
+ */
+PhoneGap.addConstructor(function() { 
+    if(typeof navigator.contacts == "undefined") {
+       navigator.contacts = new Contacts();
+    }
+});
+};
+if (!PhoneGap.hasResource("file")) {
+       PhoneGap.addResource("file");
+
+/**
+ * This class provides generic read and write access to the mobile device file system.
+ * They are not used to read files from a server.
+ */
+
+/**
+ * This class provides some useful information about a file.
+ * This is the fields returned when navigator.fileMgr.getFileProperties() 
+ * is called.
+ */
+FileProperties = function(filePath) {
+    this.filePath = filePath;
+    this.size = 0;
+    this.lastModifiedDate = null;
+}
+/**
+ * Represents a single file.
+ * 
+ * name {DOMString} name of the file, without path information
+ * fullPath {DOMString} the full path of the file, including the name
+ * type {DOMString} mime type
+ * lastModifiedDate {Date} last modified date
+ * size {Number} size of the file in bytes
+ */
+File = function(name, fullPath, type, lastModifiedDate, size) {
+       this.name = name || null;
+    this.fullPath = fullPath || null;
+       this.type = type || null;
+    this.lastModifiedDate = lastModifiedDate || null;
+    this.size = size || 0;
+}
+/**
+ * Create an event object since we can't set target on DOM event.
+ *
+ * @param type
+ * @param target
+ *
+ */
+File._createEvent = function(type, target) {
+    // Can't create event object, since we can't set target (its readonly)
+    //var evt = document.createEvent('Events');
+    //evt.initEvent("onload", false, false);
+    var evt = {"type": type};
+    evt.target = target;
+    return evt;
+};
+
+FileError = function() {
+   this.code = null;
+}
+
+// File error codes
+// Found in DOMException
+FileError.NOT_FOUND_ERR = 1;
+FileError.SECURITY_ERR = 2;
+FileError.ABORT_ERR = 3;
+
+// Added by this specification
+FileError.NOT_READABLE_ERR = 4;
+FileError.ENCODING_ERR = 5;
+FileError.NO_MODIFICATION_ALLOWED_ERR = 6;
+FileError.INVALID_STATE_ERR = 7;
+FileError.SYNTAX_ERR = 8;
+FileError.INVALID_MODIFICATION_ERR = 9;
+FileError.QUOTA_EXCEEDED_ERR = 10;
+FileError.TYPE_MISMATCH_ERR = 11;
+FileError.PATH_EXISTS_ERR = 12;
+
+//-----------------------------------------------------------------------------
+// File manager
+//-----------------------------------------------------------------------------
+
+FileMgr = function() {
+}
+
+FileMgr.prototype.testFileExists = function(fileName, successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "testFileExists", [fileName]);
+};
+
+FileMgr.prototype.testDirectoryExists = function(dirName, successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "testDirectoryExists", [dirName]);
+};
+
+FileMgr.prototype.getFreeDiskSpace = function(successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "getFreeDiskSpace", []);
+};
+
+FileMgr.prototype.write = function(fileName, data, position, successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "write", [fileName, data, position]);
+};
+
+FileMgr.prototype.truncate = function(fileName, size, successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "truncateFile", [fileName, size]);
+};
+
+FileMgr.prototype.readAsText = function(fileName, encoding, successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "readFile", [fileName, encoding]);
+};
+
+FileMgr.prototype.readAsDataURL = function(fileName, successCallback, errorCallback) {
+       PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "readAsDataURL", [fileName]);
+};
+
+PhoneGap.addConstructor(function() {
+    if (typeof navigator.fileMgr === "undefined") {
+        navigator.fileMgr = new FileMgr();
+    }
+});
+
+
+//-----------------------------------------------------------------------------
+// File Reader
+//-----------------------------------------------------------------------------
+
+/**
+ * This class reads the mobile device file system.
+ *
+ */
+FileReader = function() {
+    this.fileName = "";
+
+    this.readyState = 0;
+
+    // File data
+    this.result = null;
+
+    // Error
+    this.error = null;
+
+    // Event handlers
+    this.onloadstart = null;    // When the read starts.
+    this.onprogress = null;     // While reading (and decoding) file or fileBlob data, and reporting partial file data (progess.loaded/progress.total)
+    this.onload = null;         // When the read has successfully completed.
+    this.onerror = null;        // When the read has failed (see errors).
+    this.onloadend = null;      // When the request has completed (either in success or failure).
+    this.onabort = null;        // When the read has been aborted. For instance, by invoking the abort() method.
+}
+
+// States
+FileReader.EMPTY = 0;
+FileReader.LOADING = 1;
+FileReader.DONE = 2;
+
+/**
+ * Abort reading file.
+ */
+FileReader.prototype.abort = function() {
+    var evt;
+    this.readyState = FileReader.DONE;
+    this.result = null;
+
+    // set error
+    var error = new FileError();
+    error.code = error.ABORT_ERR;
+    this.error = error;
+   
+    // If error callback
+    if (typeof this.onerror === "function") {
+        evt = File._createEvent("error", this);
+        this.onerror(evt);
+    }
+    // If abort callback
+    if (typeof this.onabort === "function") {
+        evt = File._createEvent("abort", this);
+        this.onabort(evt);
+    }
+    // If load end callback
+    if (typeof this.onloadend === "function") {
+        evt = File._createEvent("loadend", this);
+        this.onloadend(evt);
+    }
+};
+
+/**
+ * Read text file.
+ *
+ * @param file          The name of the file
+ * @param encoding      [Optional] (see http://www.iana.org/assignments/character-sets)
+ */
+FileReader.prototype.readAsText = function(file, encoding) {
+    this.fileName = "";
+       if (typeof file.fullPath === "undefined") {
+               this.fileName = file;
+       } else {
+               this.fileName = file.fullPath;
+       }
+
+    // LOADING state
+    this.readyState = FileReader.LOADING;
+
+    // If loadstart callback
+    if (typeof this.onloadstart === "function") {
+        var evt = File._createEvent("loadstart", this);
+        this.onloadstart(evt);
+    }
+
+    // Default encoding is UTF-8
+    var enc = encoding ? encoding : "UTF-8";
+
+    var me = this;
+
+    // Read file
+    navigator.fileMgr.readAsText(this.fileName, enc,
+
+        // Success callback
+        function(r) {
+            var evt;
+
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileReader.DONE) {
+                return;
+            }
+
+            // Save result
+            me.result = decodeURIComponent(r);
+
+            // If onload callback
+            if (typeof me.onload === "function") {
+                evt = File._createEvent("load", me);
+                me.onload(evt);
+            }
+
+            // DONE state
+            me.readyState = FileReader.DONE;
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                evt = File._createEvent("loadend", me);
+                me.onloadend(evt);
+            }
+        },
+
+        // Error callback
+        function(e) {
+            var evt;
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileReader.DONE) {
+                return;
+            }
+
+            // Save error
+            me.error = e;
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                evt = File._createEvent("error", me);
+                me.onerror(evt);
+            }
+
+            // DONE state
+            me.readyState = FileReader.DONE;
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                evt = File._createEvent("loadend", me);
+                me.onloadend(evt);
+            }
+        }
+        );
+};
+
+
+/**
+ * Read file and return data as a base64 encoded data url.
+ * A data url is of the form:
+ *      data:[<mediatype>][;base64],<data>
+ *
+ * @param file          {File} File object containing file properties
+ */
+FileReader.prototype.readAsDataURL = function(file) {
+    this.fileName = "";
+    
+    if (typeof file.fullPath === "undefined") {
+        this.fileName = file;
+    } else {
+        this.fileName = file.fullPath;
+    }
+
+    // LOADING state
+    this.readyState = FileReader.LOADING;
+
+    // If loadstart callback
+    if (typeof this.onloadstart === "function") {
+        var evt = File._createEvent("loadstart", this);
+        this.onloadstart(evt);
+    }
+
+    var me = this;
+
+    // Read file
+    navigator.fileMgr.readAsDataURL(this.fileName,
+
+        // Success callback
+        function(r) {
+            var evt;
+
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileReader.DONE) {
+                return;
+            }
+
+            // Save result
+            me.result = r;
+
+            // If onload callback
+            if (typeof me.onload === "function") {
+                evt = File._createEvent("load", me);
+                me.onload(evt);
+            }
+
+            // DONE state
+            me.readyState = FileReader.DONE;
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                evt = File._createEvent("loadend", me);
+                me.onloadend(evt);
+            }
+        },
+
+        // Error callback
+        function(e) {
+            var evt;
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileReader.DONE) {
+                return;
+            }
+
+            // Save error
+            me.error = e;
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                evt = File._createEvent("error", me);
+                me.onerror(evt);
+            }
+
+            // DONE state
+            me.readyState = FileReader.DONE;
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                evt = File._createEvent("loadend", me);
+                me.onloadend(evt);
+            }
+        }
+        );
+};
+
+/**
+ * Read file and return data as a binary data.
+ *
+ * @param file          The name of the file
+ */
+FileReader.prototype.readAsBinaryString = function(file) {
+    // TODO - Can't return binary data to browser.
+    this.fileName = file;
+};
+
+/**
+ * Read file and return data as a binary data.
+ *
+ * @param file          The name of the file
+ */
+FileReader.prototype.readAsArrayBuffer = function(file) {
+    // TODO - Can't return binary data to browser.
+    this.fileName = file;
+};
+
+//-----------------------------------------------------------------------------
+// File Writer
+//-----------------------------------------------------------------------------
+
+/**
+ * This class writes to the mobile device file system.
+ *
+  @param file {File} a File object representing a file on the file system
+*/
+FileWriter = function(file) {
+    this.fileName = "";
+    this.length = 0;
+       if (file) {
+           this.fileName = file.fullPath || file;
+           this.length = file.size || 0;
+       }
+       
+       // default is to write at the beginning of the file
+    this.position = 0;
+    
+    this.readyState = 0; // EMPTY
+
+    this.result = null;
+
+    // Error
+    this.error = null;
+
+    // Event handlers
+    this.onwritestart = null;  // When writing starts
+    this.onprogress = null;            // While writing the file, and reporting partial file data
+    this.onwrite = null;               // When the write has successfully completed.
+    this.onwriteend = null;            // When the request has completed (either in success or failure).
+    this.onabort = null;               // When the write has been aborted. For instance, by invoking the abort() method.
+    this.onerror = null;               // When the write has failed (see errors).
+}
+
+// States
+FileWriter.INIT = 0;
+FileWriter.WRITING = 1;
+FileWriter.DONE = 2;
+
+/**
+ * Abort writing file.
+ */
+FileWriter.prototype.abort = function() {
+    // check for invalid state
+       if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) {
+               throw FileError.INVALID_STATE_ERR;
+       } 
+
+    // set error
+    var error = new FileError(), evt;
+    error.code = error.ABORT_ERR;
+    this.error = error;
+    
+    // If error callback
+    if (typeof this.onerror === "function") {
+        evt = File._createEvent("error", this);
+        this.onerror(evt);
+    }
+    // If abort callback
+    if (typeof this.onabort === "function") {
+        evt = File._createEvent("abort", this);
+        this.onabort(evt);
+    }
+    
+    this.readyState = FileWriter.DONE;
+
+    // If write end callback
+    if (typeof this.onwriteend == "function") {
+        evt = File._createEvent("writeend", this);
+        this.onwriteend(evt);
+    }
+};
+
+/**
+ * @Deprecated: use write instead
+ * 
+ * @param file to write the data to
+ * @param text to be written
+ * @param bAppend if true write to end of file, otherwise overwrite the file
+ */
+FileWriter.prototype.writeAsText = function(file, text, bAppend) {
+       // Throw an exception if we are already writing a file
+       if (this.readyState === FileWriter.WRITING) {
+               throw FileError.INVALID_STATE_ERR;
+       }
+
+       if (bAppend !== true) {
+        bAppend = false; // for null values
+    }
+
+    this.fileName = file;
+
+    // WRITING state
+    this.readyState = FileWriter.WRITING;
+
+    var me = this;
+
+    // If onwritestart callback
+    if (typeof me.onwritestart === "function") {
+        var evt = File._createEvent("writestart", me);
+        me.onwritestart(evt);
+    }
+       
+       
+    // Write file 
+       navigator.fileMgr.writeAsText(file, text, bAppend,
+        // Success callback
+        function(r) {
+            var evt;
+
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileWriter.DONE) {
+                return;
+            }
+
+            // Save result
+            me.result = r;
+
+            // If onwrite callback
+            if (typeof me.onwrite === "function") {
+                evt = File._createEvent("write", me);
+                me.onwrite(evt);
+            }
+
+            // DONE state
+            me.readyState = FileWriter.DONE;
+
+            // If onwriteend callback
+            if (typeof me.onwriteend === "function") {
+                evt = File._createEvent("writeend", me);
+                me.onwriteend(evt);
+            }
+        },
+
+        // Error callback
+        function(e) {
+            var evt;
+
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileWriter.DONE) {
+                return;
+            }
+
+            // Save error
+            me.error = e;
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                evt = File._createEvent("error", me);
+                me.onerror(evt);
+            }
+
+            // DONE state
+            me.readyState = FileWriter.DONE;
+
+            // If onwriteend callback
+            if (typeof me.onwriteend === "function") {
+                evt = File._createEvent("writeend", me);
+                me.onwriteend(evt);
+            }
+        }
+    );
+};
+
+/**
+ * Writes data to the file
+ *  
+ * @param text to be written
+ */
+FileWriter.prototype.write = function(text) {
+       // Throw an exception if we are already writing a file
+       if (this.readyState === FileWriter.WRITING) {
+               throw FileError.INVALID_STATE_ERR;
+       }
+
+    // WRITING state
+    this.readyState = FileWriter.WRITING;
+
+    var me = this;
+
+    // If onwritestart callback
+    if (typeof me.onwritestart === "function") {
+        var evt = File._createEvent("writestart", me);
+        me.onwritestart(evt);
+    }
+
+    // Write file
+    navigator.fileMgr.write(this.fileName, text, this.position,
+
+        // Success callback
+        function(r) {
+            var evt;
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileWriter.DONE) {
+                return;
+            }
+
+            
+            // position always increases by bytes written because file would be extended
+            me.position += r;
+                       // The length of the file is now where we are done writing.
+                       me.length = me.position;
+            
+            // If onwrite callback
+            if (typeof me.onwrite === "function") {
+                evt = File._createEvent("write", me);
+                me.onwrite(evt);
+            }
+
+            // DONE state
+            me.readyState = FileWriter.DONE;
+
+            // If onwriteend callback
+            if (typeof me.onwriteend === "function") {
+                evt = File._createEvent("writeend", me);
+                me.onwriteend(evt);
+            }
+        },
+
+        // Error callback
+        function(e) {
+            var evt;
+
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileWriter.DONE) {
+                return;
+            }
+
+            // Save error
+            me.error = e;
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                evt = File._createEvent("error", me);
+                me.onerror(evt);
+            }
+
+            // DONE state
+            me.readyState = FileWriter.DONE;
+
+            // If onwriteend callback
+            if (typeof me.onwriteend === "function") {
+                evt = File._createEvent("writeend", me);
+                me.onwriteend(evt);
+            }
+        }
+        );
+
+};
+
+/** 
+ * Moves the file pointer to the location specified.
+ * 
+ * If the offset is a negative number the position of the file 
+ * pointer is rewound.  If the offset is greater than the file 
+ * size the position is set to the end of the file.  
+ * 
+ * @param offset is the location to move the file pointer to.
+ */
+FileWriter.prototype.seek = function(offset) {
+    // Throw an exception if we are already writing a file
+    if (this.readyState === FileWriter.WRITING) {
+        throw FileError.INVALID_STATE_ERR;
+    }
+
+    if (!offset) {
+        return;
+    }
+    
+    // See back from end of file.
+    if (offset < 0) {
+               this.position = Math.max(offset + this.length, 0);
+       }
+    // Offset is bigger then file size so set position 
+    // to the end of the file.
+       else if (offset > this.length) {
+               this.position = this.length;
+       }
+    // Offset is between 0 and file size so set the position
+    // to start writing.
+       else {
+               this.position = offset;
+       }       
+};
+
+/** 
+ * Truncates the file to the size specified.
+ * 
+ * @param size to chop the file at.
+ */
+FileWriter.prototype.truncate = function(size) {
+       // Throw an exception if we are already writing a file
+       if (this.readyState === FileWriter.WRITING) {
+               throw FileError.INVALID_STATE_ERR;
+       }
+       // what if no size specified? 
+
+    // WRITING state
+    this.readyState = FileWriter.WRITING;
+
+    var me = this;
+
+    // If onwritestart callback
+    if (typeof me.onwritestart === "function") {
+        var evt = File._createEvent("writestart", me);
+        me.onwritestart(evt);
+    }
+
+    // Write file
+    navigator.fileMgr.truncate(this.fileName, size,
+
+        // Success callback
+        function(r) {
+            var evt;
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileWriter.DONE) {
+                return;
+            }
+
+            // Update the length of the file
+            me.length = r;
+            me.position = Math.min(me.position, r);
+
+            // If onwrite callback
+            if (typeof me.onwrite === "function") {
+                evt = File._createEvent("write", me);
+                me.onwrite(evt);
+            }
+
+            // DONE state
+            me.readyState = FileWriter.DONE;
+
+            // If onwriteend callback
+            if (typeof me.onwriteend === "function") {
+                evt = File._createEvent("writeend", me);
+                me.onwriteend(evt);
+            }
+        },
+
+        // Error callback
+        function(e) {
+            var evt;
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileWriter.DONE) {
+                return;
+            }
+
+            // Save error
+            me.error = e;
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                evt = File._createEvent("error", me);
+                me.onerror(evt);
+            }
+
+            // DONE state
+            me.readyState = FileWriter.DONE;
+
+            // If onwriteend callback
+            if (typeof me.onwriteend === "function") {
+                evt = File._createEvent("writeend", me);
+                me.onwriteend(evt);
+            }
+        }
+    );
+};
+
+LocalFileSystem = function() {
+};
+
+// File error codes
+LocalFileSystem.TEMPORARY = 0;
+LocalFileSystem.PERSISTENT = 1;
+LocalFileSystem.RESOURCE = 2;
+LocalFileSystem.APPLICATION = 3;
+
+/**
+ * Requests a filesystem in which to store application data.
+ * 
+ * @param {int} type of file system being requested
+ * @param {Function} successCallback is called with the new FileSystem
+ * @param {Function} errorCallback is called with a FileError
+ */
+LocalFileSystem.prototype.requestFileSystem = function(type, size, successCallback, errorCallback) {
+       if (type < 0 || type > 3) {
+               if (typeof errorCallback == "function") {
+                       errorCallback({
+                               "code": FileError.SYNTAX_ERR
+                       });
+               }
+       }
+       else {
+               PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "requestFileSystem", [type, size]);
+       }
+};
+
+/**
+ * 
+ * @param {DOMString} uri referring to a local file in a filesystem
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+LocalFileSystem.prototype.resolveLocalFileSystemURI = function(uri, successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "resolveLocalFileSystemURI", [uri]);
+};
+
+/**
+* This function  is required as we need to convert raw 
+* JSON objects into concrete File and Directory objects.  
+* 
+* @param a JSON Objects that need to be converted to DirectoryEntry or FileEntry objects.
+* @returns an entry 
+*/
+LocalFileSystem.prototype._castFS = function(pluginResult) {
+    var entry = null;
+    entry = new DirectoryEntry();
+    entry.isDirectory = pluginResult.message.root.isDirectory;
+    entry.isFile = pluginResult.message.root.isFile;
+    entry.name = pluginResult.message.root.name;
+    entry.fullPath = pluginResult.message.root.fullPath;
+    pluginResult.message.root = entry;
+    return pluginResult;    
+}
+
+LocalFileSystem.prototype._castEntry = function(pluginResult) {
+    var entry = null;
+    if (pluginResult.message.isDirectory) {
+        entry = new DirectoryEntry();
+    }
+    else if (pluginResult.message.isFile) {
+               entry = new FileEntry();
+    }
+    entry.isDirectory = pluginResult.message.isDirectory;
+    entry.isFile = pluginResult.message.isFile;
+    entry.name = pluginResult.message.name;
+    entry.fullPath = pluginResult.message.fullPath;
+    pluginResult.message = entry;
+    return pluginResult;    
+}
+
+LocalFileSystem.prototype._castEntries = function(pluginResult) {
+    var entries = pluginResult.message;
+       var retVal = []; 
+       for (i=0; i<entries.length; i++) {
+               retVal.push(window.localFileSystem._createEntry(entries[i]));
+       }
+    pluginResult.message = retVal;
+    return pluginResult;    
+}
+
+LocalFileSystem.prototype._createEntry = function(castMe) {
+       var entry = null;
+    if (castMe.isDirectory) {
+        entry = new DirectoryEntry();
+    }
+    else if (castMe.isFile) {
+        entry = new FileEntry();
+    }
+    entry.isDirectory = castMe.isDirectory;
+    entry.isFile = castMe.isFile;
+    entry.name = castMe.name;
+    entry.fullPath = castMe.fullPath;
+    return entry;    
+
+}
+
+LocalFileSystem.prototype._castDate = function(pluginResult) {
+       if (pluginResult.message.modificationTime) {
+               var metadataObj = new Metadata();
+               
+           metadataObj.modificationTime = new Date(pluginResult.message.modificationTime);
+           pluginResult.message = metadataObj;
+       }
+       else if (pluginResult.message.lastModifiedDate) {
+               var file = new File();
+        file.size = pluginResult.message.size;
+        file.type = pluginResult.message.type;
+        file.name = pluginResult.message.name;
+        file.fullPath = pluginResult.message.fullPath;
+               file.lastModifiedDate = new Date(pluginResult.message.lastModifiedDate);
+           pluginResult.message = file;                
+       }
+
+    return pluginResult;       
+}
+LocalFileSystem.prototype._castError = function(pluginResult) {
+       var fileError = new FileError();
+       fileError.code = pluginResult.message;
+       pluginResult.message = fileError;
+       return pluginResult;
+}
+
+/**
+ * Information about the state of the file or directory
+ * 
+ * {Date} modificationTime (readonly)
+ */
+Metadata = function() {
+    this.modificationTime=null;
+};
+
+/**
+ * Supplies arguments to methods that lookup or create files and directories
+ * 
+ * @param {boolean} create file or directory if it doesn't exist 
+ * @param {boolean} exclusive if true the command will fail if the file or directory exists
+ */
+Flags = function(create, exclusive) {
+    this.create = create || false;
+    this.exclusive = exclusive || false;
+};
+
+/**
+ * An interface representing a file system
+ * 
+ * {DOMString} name the unique name of the file system (readonly)
+ * {DirectoryEntry} root directory of the file system (readonly)
+ */
+FileSystem = function() {
+    this.name = null;
+    this.root = null;
+};
+
+/**
+ * An interface representing a directory on the file system.
+ * 
+ * {boolean} isFile always false (readonly)
+ * {boolean} isDirectory always true (readonly)
+ * {DOMString} name of the directory, excluding the path leading to it (readonly)
+ * {DOMString} fullPath the absolute full path to the directory (readonly)
+ * {FileSystem} filesystem on which the directory resides (readonly)
+ */
+DirectoryEntry = function() {
+    this.isFile = false;
+    this.isDirectory = true;
+    this.name = null;
+    this.fullPath = null;
+    this.filesystem = null;
+};
+
+/**
+ * Copies a directory to a new location
+ * 
+ * @param {DirectoryEntry} parent the directory to which to copy the entry
+ * @param {DOMString} newName the new name of the entry, defaults to the current name
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.copyTo = function(parent, newName, successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "copyTo", [this.fullPath, parent, newName]);
+};
+
+/**
+ * Looks up the metadata of the entry
+ * 
+ * @param {Function} successCallback is called with a Metadata object
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getMetadata = function(successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "getMetadata", [this.fullPath]);
+};
+
+/**
+ * Gets the parent of the entry
+ * 
+ * @param {Function} successCallback is called with a parent entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getParent = function(successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "getParent", [this.fullPath]);
+};
+
+/**
+ * Moves a directory to a new location
+ * 
+ * @param {DirectoryEntry} parent the directory to which to move the entry
+ * @param {DOMString} newName the new name of the entry, defaults to the current name
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.moveTo = function(parent, newName, successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "moveTo", [this.fullPath, parent, newName]);
+};
+
+/**
+ * Removes the entry
+ * 
+ * @param {Function} successCallback is called with no parameters
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.remove = function(successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "remove", [this.fullPath]);
+};
+
+/**
+ * Returns a URI that can be used to identify this entry.
+ * 
+ * @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI.
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.toURI = function(mimeType, successCallback, errorCallback) {
+    return "file://localhost" + this.fullPath;
+    //PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "toURI", [this.fullPath, mimeType]);
+};
+
+/**
+ * Creates a new DirectoryReader to read entries from this directory
+ */
+DirectoryEntry.prototype.createReader = function(successCallback, errorCallback) {
+    return new DirectoryReader(this.fullPath);
+};
+
+/**
+ * Creates or looks up a directory
+ * 
+ * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory
+ * @param {Flags} options to create or excluively create the directory
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "getDirectory", [this.fullPath, path, options]);
+};
+
+/**
+ * Creates or looks up a file
+ * 
+ * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file
+ * @param {Flags} options to create or excluively create the file
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "getFile", [this.fullPath, path, options]);
+};
+
+/**
+ * Deletes a directory and all of it's contents
+ * 
+ * @param {Function} successCallback is called with no parameters
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "removeRecursively", [this.fullPath]);
+};
+
+/**
+ * An interface that lists the files and directories in a directory.
+ */
+DirectoryReader = function(fullPath){
+       this.fullPath = fullPath || null;    
+};
+
+/**
+ * Returns a list of entries from a directory.
+ * 
+ * @param {Function} successCallback is called with a list of entries
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "readEntries", [this.fullPath]);
+}
+/**
+ * An interface representing a directory on the file system.
+ * 
+ * {boolean} isFile always true (readonly)
+ * {boolean} isDirectory always false (readonly)
+ * {DOMString} name of the file, excluding the path leading to it (readonly)
+ * {DOMString} fullPath the absolute full path to the file (readonly)
+ * {FileSystem} filesystem on which the directory resides (readonly)
+ */
+FileEntry = function() {
+    this.isFile = true;
+    this.isDirectory = false;
+    this.name = null;
+    this.fullPath = null;
+    this.filesystem = null;
+};
+
+/**
+ * Copies a file to a new location
+ * 
+ * @param {DirectoryEntry} parent the directory to which to copy the entry
+ * @param {DOMString} newName the new name of the entry, defaults to the current name
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.copyTo = function(parent, newName, successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "copyTo", [this.fullPath, parent, newName]);
+};
+
+/**
+ * Looks up the metadata of the entry
+ * 
+ * @param {Function} successCallback is called with a Metadata object
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.getMetadata = function(successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "getMetadata", [this.fullPath]);
+};
+
+/**
+ * Gets the parent of the entry
+ * 
+ * @param {Function} successCallback is called with a parent entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.getParent = function(successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "getParent", [this.fullPath]);
+};
+
+/**
+ * Moves a directory to a new location
+ * 
+ * @param {DirectoryEntry} parent the directory to which to move the entry
+ * @param {DOMString} newName the new name of the entry, defaults to the current name
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.moveTo = function(parent, newName, successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "moveTo", [this.fullPath, parent, newName]);
+};
+
+/**
+ * Removes the entry
+ * 
+ * @param {Function} successCallback is called with no parameters
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.remove = function(successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "remove", [this.fullPath]);
+};
+
+/**
+ * Returns a URI that can be used to identify this entry.
+ * 
+ * @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI.
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.toURI = function(mimeType, successCallback, errorCallback) {
+    return "file://localhost" + this.fullPath;
+    //PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "toURI", [this.fullPath, mimeType]);
+};
+
+/**
+ * Creates a new FileWriter associated with the file that this FileEntry represents.
+ * 
+ * @param {Function} successCallback is called with the new FileWriter
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.createWriter = function(successCallback, errorCallback) {
+       this.file(function(filePointer) {       
+               var writer = new FileWriter(filePointer);
+               if (writer.fileName == null || writer.fileName == "") {
+                       if (typeof errorCallback == "function") {
+                               errorCallback({
+                                       "code": FileError.INVALID_STATE_ERR
+                               });
+               }
+               }
+               if (typeof successCallback == "function") {
+                       successCallback(writer);
+               }       
+       }, errorCallback);
+};
+
+/**
+ * Returns a File that represents the current state of the file that this FileEntry represents.
+ * 
+ * @param {Function} successCallback is called with the new File object
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.file = function(successCallback, errorCallback) {
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "getFileMetadata", [this.fullPath]);
+};
+
+/**
+ * Add the FileSystem interface into the browser.
+ */
+PhoneGap.addConstructor(function() {
+       var pgLocalFileSystem = new LocalFileSystem();
+       // Needed for cast methods
+    if(typeof window.localFileSystem == "undefined") window.localFileSystem  = pgLocalFileSystem;
+    if(typeof window.requestFileSystem == "undefined") window.requestFileSystem  = pgLocalFileSystem.requestFileSystem;
+    if(typeof window.resolveLocalFileSystemURI == "undefined") window.resolveLocalFileSystemURI = pgLocalFileSystem.resolveLocalFileSystemURI;
+});
+};
+
+
+
+
+/*
+ * PhoneGap is available under *either* the terms of the modified BSD license *or* the
+ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
+ *  
+ * Copyright (c) 2005-2011, Nitobi Software Inc.
+ * Copyright (c) 2011, Matt Kane
+ */
+
+if (!PhoneGap.hasResource("filetransfer")) {
+       PhoneGap.addResource("filetransfer");
+
+/**
+ * FileTransfer uploads a file to a remote server.
+ */
+FileTransfer = function() {}
+
+/**
+ * FileUploadResult
+ */
+FileUploadResult = function() {
+    this.bytesSent = 0;
+    this.responseCode = null;
+    this.response = null;
+}
+
+/**
+ * FileTransferError
+ */
+FileTransferError = function(errorCode) {
+    this.code = errorCode || null;
+}
+
+FileTransferError.FILE_NOT_FOUND_ERR = 1;
+FileTransferError.INVALID_URL_ERR = 2;
+FileTransferError.CONNECTION_ERR = 3;
+
+/**
+* Given an absolute file path, uploads a file on the device to a remote server 
+* using a multipart HTTP request.
+* @param filePath {String}           Full path of the file on the device
+* @param server {String}             URL of the server to receive the file
+* @param successCallback (Function}  Callback to be invoked when upload has completed
+* @param errorCallback {Function}    Callback to be invoked upon error
+* @param options {FileUploadOptions} Optional parameters such as file name and mimetype           
+*/
+FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options) {
+       if(!options.params) {
+               options.params = {};
+       }
+       options.filePath = filePath;
+       options.server = server;
+       if(!options.fileKey) {
+               options.fileKey = 'file';
+       }
+       if(!options.fileName) {
+               options.fileName = 'image.jpg';
+       }
+       if(!options.mimeType) {
+               options.mimeType = 'image/jpeg';
+       }
+       
+       // successCallback required
+       if (typeof successCallback != "function") {
+        console.log("FileTransfer Error: successCallback is not a function");
+        return;
+    }
+
+
+    // errorCallback optional
+    if (errorCallback && (typeof errorCallback != "function")) {
+        console.log("FileTransfer Error: errorCallback is not a function");
+        return;
+    }
+       
+    PhoneGap.exec(successCallback, errorCallback, 'com.phonegap.filetransfer', 'upload', [options]);
+};
+
+FileTransfer.prototype._castTransferError = function(pluginResult) {
+       var fileError = new FileTransferError(pluginResult.message);
+       //fileError.code = pluginResult.message;
+       pluginResult.message = fileError;
+       return pluginResult;
+}
+
+FileTransfer.prototype._castUploadResult = function(pluginResult) {
+       var result = new FileUploadResult();
+       result.bytesSent = pluginResult.message.bytesSent;
+       result.responseCode = pluginResult.message.responseCode;
+       result.response = decodeURIComponent(pluginResult.message.response);
+       pluginResult.message = result;
+       return pluginResult;
+}
+
+/**
+ * Options to customize the HTTP request used to upload files.
+ * @param fileKey {String}   Name of file request parameter.
+ * @param fileName {String}  Filename to be used by the server. Defaults to image.jpg.
+ * @param mimeType {String}  Mimetype of the uploaded file. Defaults to image/jpeg.
+ * @param params {Object}    Object with key: value params to send to the server.
+ */
+FileUploadOptions = function(fileKey, fileName, mimeType, params) {
+    this.fileKey = fileKey || null;
+    this.fileName = fileName || null;
+    this.mimeType = mimeType || null;
+    this.params = params || null;
+}
+
+
+PhoneGap.addConstructor(function() {
+    if (typeof navigator.fileTransfer == "undefined") navigator.fileTransfer = new FileTransfer();
+});
+};
+if (!PhoneGap.hasResource("geolocation")) {
+       PhoneGap.addResource("geolocation");
+
+/**
+ * This class provides access to device GPS data.
+ * @constructor
+ */
+Geolocation = function() {
+    // The last known GPS position.
+    this.lastPosition = null;
+    this.listener = null;
+    this.timeoutTimerId = 0;
+
+};
+
+
+/**
+ * Asynchronously aquires the current position.
+ * @param {Function} successCallback The function to call when the position
+ * data is available
+ * @param {Function} errorCallback The function to call when there is an error 
+ * getting the position data.
+ * @param {PositionOptions} options The options for getting the position data
+ * such as timeout.
+ * PositionOptions.forcePrompt:Bool default false, 
+ * - tells iPhone to prompt the user to turn on location services.
+ * - may cause your app to exit while the user is sent to the Settings app
+ * PositionOptions.distanceFilter:double aka Number
+ * - used to represent a distance in meters.
+PositionOptions
+{
+   desiredAccuracy:Number
+   - a distance in meters 
+               < 10   = best accuracy  ( Default value )
+               < 100  = Nearest Ten Meters
+               < 1000 = Nearest Hundred Meters
+               < 3000 = Accuracy Kilometers
+               3000+  = Accuracy 3 Kilometers
+               
+       forcePrompt:Boolean default false ( iPhone Only! )
+    - tells iPhone to prompt the user to turn on location services.
+       - may cause your app to exit while the user is sent to the Settings app
+       
+       distanceFilter:Number
+       - The minimum distance (measured in meters) a device must move laterally before an update event is generated.
+       - measured relative to the previously delivered location
+       - default value: null ( all movements will be reported )
+       
+}
+
+ */
+Geolocation.prototype.getCurrentPosition = function(successCallback, errorCallback, options) 
+{
+    // create an always valid local success callback
+    var win = successCallback;
+    if (!win || typeof(win) != 'function')
+    {
+        win = function(position) {};
+    }
+    
+    // create an always valid local error callback
+    var fail = errorCallback;
+    if (!fail || typeof(fail) != 'function')
+    {
+        fail = function(positionError) {};
+    }  
+
+    var self = this;
+    var totalTime = 0;
+       var timeoutTimerId;
+       
+       // set params to our default values
+       var params = new PositionOptions();
+       
+    if (options) 
+    {
+        if (options.maximumAge) 
+        {
+            // special case here if we have a cached value that is younger than maximumAge
+            if(this.lastPosition)
+            {
+                var now = new Date().getTime();
+                if((now - this.lastPosition.timestamp) < options.maximumAge)
+                {
+                    win(this.lastPosition); // send cached position immediately 
+                    return;                 // Note, execution stops here -jm
+                }
+            }
+            params.maximumAge = options.maximumAge;
+        }
+        if (options.enableHighAccuracy) 
+        {
+            params.enableHighAccuracy = (options.enableHighAccuracy == true); // make sure it's truthy
+        }
+        if (options.timeout) 
+        {
+            params.timeout = options.timeout;
+        }
+    }
+    
+    this.listener = {"success":win,"fail":fail};
+    this.start(params);
+       
+       var onTimeout = function()
+       {
+           self.setError(new PositionError(PositionError.TIMEOUT,"Geolocation Error: Timeout."));
+       };
+
+    clearTimeout(this.timeoutTimerId);
+    this.timeoutTimerId = setTimeout(onTimeout, params.timeout); 
+};
+
+/**
+ * Asynchronously aquires the position repeatedly at a given interval.
+ * @param {Function} successCallback The function to call each time the position
+ * data is available
+ * @param {Function} errorCallback The function to call when there is an error 
+ * getting the position data.
+ * @param {PositionOptions} options The options for getting the position data
+ * such as timeout and the frequency of the watch.
+ */
+Geolocation.prototype.watchPosition = function(successCallback, errorCallback, options) {
+       // Invoke the appropriate callback with a new Position object every time the implementation 
+       // determines that the position of the hosting device has changed. 
+
+       var self = this; // those == this & that
+       
+       var params = new PositionOptions();
+
+    if(options)
+    {
+        if (options.maximumAge) {
+            params.maximumAge = options.maximumAge;
+        }
+        if (options.enableHighAccuracy) {
+            params.enableHighAccuracy = options.enableHighAccuracy;
+        }
+        if (options.timeout) {
+            params.timeout = options.timeout;
+        }
+    }
+
+       var that = this;
+    var lastPos = that.lastPosition? that.lastPosition.clone() : null;
+    
+       var intervalFunction = function() {
+        
+               var filterFun = function(position) {
+            if (lastPos == null || !position.equals(lastPos)) {
+                // only call the success callback when there is a change in position, per W3C
+                successCallback(position);
+            }
+            
+            // clone the new position, save it as our last position (internal var)
+            lastPos = position.clone();
+        };
+               
+               that.getCurrentPosition(filterFun, errorCallback, params);
+       };
+       
+    // Retrieve location immediately and schedule next retrieval afterwards
+       intervalFunction();
+       
+       return setInterval(intervalFunction, params.timeout);
+};
+
+
+/**
+ * Clears the specified position watch.
+ * @param {String} watchId The ID of the watch returned from #watchPosition.
+ */
+Geolocation.prototype.clearWatch = function(watchId) {
+       clearInterval(watchId);
+};
+
+/**
+ * Called by the geolocation framework when the current location is found.
+ * @param {PositionOptions} position The current position.
+ */
+Geolocation.prototype.setLocation = function(position) 
+{
+    var _position = new Position(position.coords, position.timestamp);
+
+    if(this.timeoutTimerId)
+    {
+        clearTimeout(this.timeoutTimerId);
+        this.timeoutTimerId = 0;
+    }
+    
+       this.lastError = null;
+    this.lastPosition = _position;
+    
+    if(this.listener && typeof(this.listener.success) == 'function')
+    {
+        this.listener.success(_position);
+    }
+    
+    this.listener = null;
+};
+
+/**
+ * Called by the geolocation framework when an error occurs while looking up the current position.
+ * @param {String} message The text of the error message.
+ */
+Geolocation.prototype.setError = function(error) 
+{
+       var _error = new PositionError(error.code, error.message);
+       
+    if(this.timeoutTimerId)
+    {
+        clearTimeout(this.timeoutTimerId);
+        this.timeoutTimerId = 0;
+    }
+    
+    this.lastError = _error;
+    // call error handlers directly
+    if(this.listener && typeof(this.listener.fail) == 'function')
+    {
+        this.listener.fail(_error);
+    }
+    this.listener = null;
+
+};
+
+Geolocation.prototype.start = function(positionOptions) 
+{
+    PhoneGap.exec(null, null, "com.phonegap.geolocation", "startLocation", [positionOptions]);
+
+};
+
+Geolocation.prototype.stop = function() 
+{
+    PhoneGap.exec(null, null, "com.phonegap.geolocation", "stopLocation", []);
+};
+
+
+PhoneGap.addConstructor(function() 
+{
+    if (typeof navigator._geo == "undefined") 
+    {
+        // replace origObj's functions ( listed in funkList ) with the same method name on proxyObj
+        // this is a workaround to prevent UIWebView/MobileSafari default implementation of GeoLocation
+        // because it includes the full page path as the title of the alert prompt
+        var __proxyObj = function (origObj,proxyObj,funkList)
+        {
+            var replaceFunk = function(org,proxy,fName)
+            { 
+                org[fName] = function()
+                { 
+                   return proxy[fName].apply(proxy,arguments); 
+                }; 
+            };
+
+            for(var v in funkList) { replaceFunk(origObj,proxyObj,funkList[v]);}
+        }
+        navigator._geo = new Geolocation();
+        __proxyObj(navigator.geolocation, navigator._geo,
+                 ["setLocation","getCurrentPosition","watchPosition",
+                  "clearWatch","setError","start","stop"]);
+
+    }
+
+});
+};
+if (!PhoneGap.hasResource("compass")) {
+       PhoneGap.addResource("compass");
+
+CompassError = function(){
+   this.code = null;
+};
+
+// Capture error codes
+CompassError.COMPASS_INTERNAL_ERR = 0;
+CompassError.COMPASS_NOT_SUPPORTED = 20;
+
+CompassHeading = function() {
+       this.magneticHeading = null;
+       this.trueHeading = null;
+       this.headingAccuracy = null;
+       this.timestamp = null;
+}      
+/**
+ * This class provides access to device Compass data.
+ * @constructor
+ */
+Compass = function() {
+    /**
+     * List of compass watch timers
+     */
+    this.timers = {};
+};
+
+/**
+ * Asynchronously acquires the current heading.
+ * @param {Function} successCallback The function to call when the heading
+ * data is available
+ * @param {Function} errorCallback The function to call when there is an error 
+ * getting the heading data.
+ * @param {PositionOptions} options The options for getting the heading data (not used).
+ */
+Compass.prototype.getCurrentHeading = function(successCallback, errorCallback, options) {
+       // successCallback required
+    if (typeof successCallback !== "function") {
+        console.log("Compass Error: successCallback is not a function");
+        return;
+    }
+
+    // errorCallback optional
+    if (errorCallback && (typeof errorCallback !== "function")) {
+        console.log("Compass Error: errorCallback is not a function");
+        return;
+    }
+
+    // Get heading
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.geolocation", "getCurrentHeading", []);
+};
+
+/**
+ * Asynchronously acquires the heading repeatedly at a given interval.
+ * @param {Function} successCallback The function to call each time the heading
+ * data is available
+ * @param {Function} errorCallback The function to call when there is an error 
+ * getting the heading data.
+ * @param {HeadingOptions} options The options for getting the heading data
+ * such as timeout and the frequency of the watch.
+ */
+Compass.prototype.watchHeading= function(successCallback, errorCallback, options) 
+{
+       // Default interval (100 msec)
+    var frequency = (options !== undefined) ? options.frequency : 100;
+
+    // successCallback required
+    if (typeof successCallback !== "function") {
+        console.log("Compass Error: successCallback is not a function");
+        return;
+    }
+
+    // errorCallback optional
+    if (errorCallback && (typeof errorCallback !== "function")) {
+        console.log("Compass Error: errorCallback is not a function");
+        return;
+    }
+
+    // Start watch timer to get headings
+    var id = PhoneGap.createUUID();
+    navigator.compass.timers[id] = setInterval(
+        function() {
+            PhoneGap.exec(successCallback, errorCallback, "com.phonegap.geolocation", "getCurrentHeading", [{repeats: 1}]);
+        }, frequency);
+
+    return id;
+};
+
+
+/**
+ * Clears the specified heading watch.
+ * @param {String} watchId The ID of the watch returned from #watchHeading.
+ */
+Compass.prototype.clearWatch = function(id) 
+{
+       // Stop javascript timer & remove from timer list
+    if (id && navigator.compass.timers[id]) {
+        clearInterval(navigator.compass.timers[id]);
+        delete navigator.compass.timers[id];
+    }
+    if (navigator.compass.timers.length == 0) {
+       // stop the 
+       PhoneGap.exec(null, null, "com.phonegap.geolocation", "stopHeading", []);
+    }
+};
+
+/** iOS only
+ * Asynchronously fires when the heading changes from the last reading.  The amount of distance 
+ * required to trigger the event is specified in the filter paramter.
+ * @param {Function} successCallback The function to call each time the heading
+ * data is available
+ * @param {Function} errorCallback The function to call when there is an error 
+ * getting the heading data.
+ * @param {HeadingOptions} options The options for getting the heading data
+ *                     @param {filter} number of degrees change to trigger a callback with heading data (float)
+ *
+ * In iOS this function is more efficient than calling watchHeading  with a frequency for updates.
+ * Only one watchHeadingFilter can be in effect at one time.  If a watchHeadingFilter is in effect, calling
+ * getCurrentHeading or watchHeading will use the existing filter value for specifying heading change. 
+  */
+Compass.prototype.watchHeadingFilter = function(successCallback, errorCallback, options) 
+{
+       if (options === undefined || options.filter === undefined) {
+               console.log("Compass Error:  options.filter not specified");
+               return;
+       }
+
+    // successCallback required
+    if (typeof successCallback !== "function") {
+        console.log("Compass Error: successCallback is not a function");
+        return;
+    }
+
+    // errorCallback optional
+    if (errorCallback && (typeof errorCallback !== "function")) {
+        console.log("Compass Error: errorCallback is not a function");
+        return;
+    }
+    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.geolocation", "watchHeadingFilter", [options]);
+}
+Compass.prototype.clearWatchFilter = function() 
+{
+       PhoneGap.exec(null, null, "com.phonegap.geolocation", "stopHeading", []);
+};
+
+PhoneGap.addConstructor(function() 
+{
+    if (typeof navigator.compass == "undefined") 
+    {
+        navigator.compass = new Compass();
+    }
+});
+};
+
+if (!PhoneGap.hasResource("media")) {
+       PhoneGap.addResource("media");
+
+/*
+ * PhoneGap is available under *either* the terms of the modified BSD license *or* the
+ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
+ *
+ * Copyright (c) 2005-2010, Nitobi Software Inc.
+ * Copyright (c) 2010,2011 IBM Corporation
+ */
+
+/**
+ * List of media objects.
+ * PRIVATE
+ */
+PhoneGap.mediaObjects = {};
+
+/**
+ * Object that receives native callbacks.
+ * PRIVATE
+ */
+PhoneGap.Media = function() {};
+
+
+/**
+ * Get the media object.
+ * PRIVATE
+ *
+ * @param id            The media object id (string)
+ */
+PhoneGap.Media.getMediaObject = function(id) {
+    return PhoneGap.mediaObjects[id];
+};
+
+/**
+ * Audio has status update.
+ * PRIVATE
+ *
+ * @param id            The media object id (string)
+ * @param msg           The status message (int)
+ * @param value        The status code (int)
+ */
+PhoneGap.Media.onStatus = function(id, msg, value) {
+    var media = PhoneGap.mediaObjects[id];
+
+    // If state update
+    if (msg == Media.MEDIA_STATE) {
+        if (value == Media.MEDIA_STOPPED) {
+            if (media.successCallback) {
+                media.successCallback();
+            }
+        }
+        if (media.statusCallback) {
+            media.statusCallback(value);
+        }
+    }
+    else if (msg == Media.MEDIA_DURATION) {
+        media._duration = value;
+    }
+    else if (msg == Media.MEDIA_ERROR) {
+        if (media.errorCallback) {
+            media.errorCallback(value);
+        }
+    }
+    else if (msg == Media.MEDIA_POSITION) {
+       media._position = value;
+    }
+};
+
+/**
+ * This class provides access to the device media, interfaces to both sound and video
+ *
+ * @param src                   The file name or url to play
+ * @param successCallback       The callback to be called when the file is done playing or recording.
+ *                                  successCallback() - OPTIONAL
+ * @param errorCallback         The callback to be called if there is an error.
+ *                                  errorCallback(int errorCode) - OPTIONAL
+ * @param statusCallback        The callback to be called when media status has changed.
+ *                                  statusCallback(int statusCode) - OPTIONAL
+ * @param positionCallback      The callback to be called when media position has changed.
+ *                                  positionCallback(long position) - OPTIONAL
+ */
+Media = function(src, successCallback, errorCallback, statusCallback, positionCallback) {
+
+    // successCallback optional
+    if (successCallback && (typeof successCallback != "function")) {
+        console.log("Media Error: successCallback is not a function");
+        return;
+    }
+
+    // errorCallback optional
+    if (errorCallback && (typeof errorCallback != "function")) {
+        console.log("Media Error: errorCallback is not a function");
+        return;
+    }
+
+    // statusCallback optional
+    if (statusCallback && (typeof statusCallback != "function")) {
+        console.log("Media Error: statusCallback is not a function");
+        return;
+    }
+
+    // positionCallback optional -- NOT SUPPORTED
+    if (positionCallback && (typeof positionCallback != "function")) {
+        console.log("Media Error: positionCallback is not a function");
+        return;
+    }
+
+    this.id = PhoneGap.createUUID();
+    PhoneGap.mediaObjects[this.id] = this;
+    this.src = src;
+    this.successCallback = successCallback;
+    this.errorCallback = errorCallback;
+    this.statusCallback = statusCallback;
+    this.positionCallback = positionCallback;
+    this._duration = -1;
+    this._position = -1;
+};
+
+// Media messages
+Media.MEDIA_STATE = 1;
+Media.MEDIA_DURATION = 2;
+Media.MEDIA_POSITION = 3;
+Media.MEDIA_ERROR = 9;
+
+// Media states
+Media.MEDIA_NONE = 0;
+Media.MEDIA_STARTING = 1;
+Media.MEDIA_RUNNING = 2;
+Media.MEDIA_PAUSED = 3;
+Media.MEDIA_STOPPED = 4;
+Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"];
+
+// TODO: Will MediaError be used?
+/**
+ * This class contains information about any Media errors.
+ * @constructor
+ */
+
+MediaError = function() {
+       this.code = null,
+       this.message = "";
+}
+
+
+MediaError.MEDIA_ERR_ABORTED        = 1;
+MediaError.MEDIA_ERR_NETWORK        = 2;
+MediaError.MEDIA_ERR_DECODE         = 3;
+MediaError.MEDIA_ERR_NONE_SUPPORTED = 4;
+
+/**
+ * Start or resume playing audio file.
+ */
+Media.prototype.play = function(options) {
+    PhoneGap.exec(null, null, "com.phonegap.media", "play", [this.id, this.src, options]);
+};
+
+/**
+ * Stop playing audio file.
+ */
+Media.prototype.stop = function() {
+    PhoneGap.exec(null, null, "com.phonegap.media","stop", [this.id, this.src]);
+};
+
+/**
+ * Pause playing audio file.
+ */
+Media.prototype.pause = function() {
+    PhoneGap.exec(null, null, "com.phonegap.media","pause", [this.id, this.src]);
+};
+
+/**
+ * Seek or jump to a new time in the track..
+ */
+Media.prototype.seekTo = function(milliseconds) {
+    PhoneGap.exec(null, null, "com.phonegap.media", "seekTo", [this.id, this.src, milliseconds]);
+};
+
+/**
+ * Get duration of an audio file.
+ * The duration is only set for audio that is playing, paused or stopped.
+ *
+ * @return      duration or -1 if not known.
+ */
+Media.prototype.getDuration = function() {
+    return this._duration;
+};
+
+/**
+ * Get position of audio.
+ *
+ * @return
+ */
+Media.prototype.getCurrentPosition = function(successCB, errorCB) {
+       var errCallback = (errorCB == undefined || errorCB == null) ? null : errorCB;
+    PhoneGap.exec(successCB, errorCB, "com.phonegap.media", "getCurrentPosition", [this.id, this.src]);
+};
+
+// iOS only.  prepare/load the audio in preparation for playing
+Media.prototype.prepare = function(successCB, errorCB) {
+       PhoneGap.exec(successCB, errorCB, "com.phonegap.media", "prepare", [this.id, this.src]);
+}
+
+/**
+ * Start recording audio file.
+ */
+Media.prototype.startRecord = function() {
+    PhoneGap.exec(null, null, "com.phonegap.media","startAudioRecord", [this.id, this.src]);
+};
+
+/**
+ * Stop recording audio file.
+ */
+Media.prototype.stopRecord = function() {
+    PhoneGap.exec(null, null, "com.phonegap.media","stopAudioRecord", [this.id, this.src]);
+};
+
+/**
+ * Release the resources.
+ */
+Media.prototype.release = function() {
+    PhoneGap.exec(null, null, "com.phonegap.media","release", [this.id, this.src]);
+};
+
+};
+if (!PhoneGap.hasResource("notification")) {
+       PhoneGap.addResource("notification");
+
+/**
+ * This class provides access to notifications on the device.
+ */
+Notification = function() {
+};
+
+/**
+ * Open a native alert dialog, with a customizable title and button text.
+ *
+ * @param {String} message              Message to print in the body of the alert
+ * @param {Function} completeCallback   The callback that is called when user clicks on a button.
+ * @param {String} title                Title of the alert dialog (default: Alert)
+ * @param {String} buttonLabel          Label of the close button (default: OK)
+ */
+Notification.prototype.alert = function(message, completeCallback, title, buttonLabel) {
+    var _title = title;
+    if (title == null || typeof title === 'undefined') {
+        _title = "Alert";
+    }
+    var _buttonLabel = (buttonLabel || "OK");
+    PhoneGap.exec(completeCallback, null, "com.phonegap.notification", "alert", [message,{ "title": _title, "buttonLabel": _buttonLabel}]);
+};
+
+/**
+ * Open a native confirm dialog, with a customizable title and button text.
+ * The result that the user selects is returned to the result callback.
+ *
+ * @param {String} message              Message to print in the body of the alert
+ * @param {Function} resultCallback     The callback that is called when user clicks on a button.
+ * @param {String} title                Title of the alert dialog (default: Confirm)
+ * @param {String} buttonLabels         Comma separated list of the labels of the buttons (default: 'OK,Cancel')
+ */
+Notification.prototype.confirm = function(message, resultCallback, title, buttonLabels) {
+    var _title = (title || "Confirm");
+    var _buttonLabels = (buttonLabels || "OK,Cancel");
+    this.alert(message, resultCallback, _title, _buttonLabels);
+};
+
+/**
+ * Causes the device to blink a status LED.
+ * @param {Integer} count The number of blinks.
+ * @param {String} colour The colour of the light.
+ */
+Notification.prototype.blink = function(count, colour) {
+// NOT IMPLEMENTED     
+};
+
+Notification.prototype.vibrate = function(mills) {
+       PhoneGap.exec(null, null, "com.phonegap.notification", "vibrate", []);
+};
+
+Notification.prototype.beep = function(count, volume) {
+       // No Volume yet for the iphone interface
+       // We can use a canned beep sound and call that
+       new Media('beep.wav').play();
+};
+
+PhoneGap.addConstructor(function() {
+    if (typeof navigator.notification == "undefined") navigator.notification = new Notification();
+});
+};
+if (!PhoneGap.hasResource("orientation")) {
+       PhoneGap.addResource("orientation");
+
+/**
+ * This class provides access to the device orientation.
+ * @constructor
+ */
+Orientation  = function() {
+       /**
+        * The current orientation, or null if the orientation hasn't changed yet.
+        */
+       this.currentOrientation = null;
+}
+
+/**
+ * Set the current orientation of the phone.  This is called from the device automatically.
+ * 
+ * When the orientation is changed, the DOMEvent \c orientationChanged is dispatched against
+ * the document element.  The event has the property \c orientation which can be used to retrieve
+ * the device's current orientation, in addition to the \c Orientation.currentOrientation class property.
+ *
+ * @param {Number} orientation The orientation to be set
+ */
+Orientation.prototype.setOrientation = function(orientation) {
+    Orientation.currentOrientation = orientation;
+    var e = document.createEvent('Events');
+    e.initEvent('orientationChanged', 'false', 'false');
+    e.orientation = orientation;
+    document.dispatchEvent(e);
+};
+
+/**
+ * Asynchronously aquires the current orientation.
+ * @param {Function} successCallback The function to call when the orientation
+ * is known.
+ * @param {Function} errorCallback The function to call when there is an error 
+ * getting the orientation.
+ */
+Orientation.prototype.getCurrentOrientation = function(successCallback, errorCallback) {
+       // If the position is available then call success
+       // If the position is not available then call error
+};
+
+/**
+ * Asynchronously aquires the orientation repeatedly at a given interval.
+ * @param {Function} successCallback The function to call each time the orientation
+ * data is available.
+ * @param {Function} errorCallback The function to call when there is an error 
+ * getting the orientation data.
+ */
+Orientation.prototype.watchOrientation = function(successCallback, errorCallback) {
+       // Invoke the appropriate callback with a new Position object every time the implementation 
+       // determines that the position of the hosting device has changed. 
+       this.getCurrentPosition(successCallback, errorCallback);
+       return setInterval(function() {
+               navigator.orientation.getCurrentOrientation(successCallback, errorCallback);
+       }, 10000);
+};
+
+/**
+ * Clears the specified orientation watch.
+ * @param {String} watchId The ID of the watch returned from #watchOrientation.
+ */
+Orientation.prototype.clearWatch = function(watchId) {
+       clearInterval(watchId);
+};
+
+Orientation.install = function()
+{
+    if (typeof navigator.orientation == "undefined") { 
+               navigator.orientation = new Orientation();
+       }
+       
+       var windowDispatchAvailable = !(window.dispatchEvent === undefined); // undefined in iOS 3.x
+       if (windowDispatchAvailable) {
+               return;
+       } 
+       
+       // the code below is to capture window.add/remove eventListener calls on window
+       // this is for iOS 3.x where listening on 'orientationchange' events don't work on document/window (don't know why)
+       // however, window.onorientationchange DOES handle the 'orientationchange' event (sent through document), so...
+       // then we multiplex the window.onorientationchange event (consequently - people shouldn't overwrite this)
+       
+       var self = this;
+       var orientationchangeEvent = 'orientationchange';
+       var newOrientationchangeEvent = 'orientationchange_pg';
+       
+       // backup original `window.addEventListener`, `window.removeEventListener`
+    var _addEventListener = window.addEventListener;
+    var _removeEventListener = window.removeEventListener;
+
+       window.onorientationchange = function() {
+               PhoneGap.fireEvent(newOrientationchangeEvent, window);
+       }
+       
+    // override `window.addEventListener`
+    window.addEventListener = function() {
+        if (arguments[0] === orientationchangeEvent) {
+                       arguments[0] = newOrientationchangeEvent; 
+               } 
+                                                                                                       
+               if (!windowDispatchAvailable) {
+                       return document.addEventListener.apply(this, arguments);
+               } else {
+                       return _addEventListener.apply(this, arguments);
+               }
+    }; 
+
+    // override `window.removeEventListener'
+    window.removeEventListener = function() {
+        if (arguments[0] === orientationchangeEvent) {
+                       arguments[0] = newOrientationchangeEvent; 
+               } 
+               
+               if (!windowDispatchAvailable) {
+                       return document.removeEventListener.apply(this, arguments);
+               } else {
+                       return _removeEventListener.apply(this, arguments);
+               }
+    }; 
+};
+
+PhoneGap.addConstructor(Orientation.install);
+
+};
+if (!PhoneGap.hasResource("sms")) {
+       PhoneGap.addResource("sms");
+
+/**
+ * This class provides access to the device SMS functionality.
+ * @constructor
+ */
+Sms = function() {
+
+}
+
+/**
+ * Sends an SMS message.
+ * @param {Integer} number The phone number to send the message to.
+ * @param {String} message The contents of the SMS message to send.
+ * @param {Function} successCallback The function to call when the SMS message is sent.
+ * @param {Function} errorCallback The function to call when there is an error sending the SMS message.
+ * @param {PositionOptions} options The options for accessing the GPS location such as timeout and accuracy.
+ */
+Sms.prototype.send = function(number, message, successCallback, errorCallback, options) {
+       // not sure why this is here when it does nothing????
+};
+
+PhoneGap.addConstructor(function() {
+    if (typeof navigator.sms == "undefined") navigator.sms = new Sms();
+});
+};
+if (!PhoneGap.hasResource("telephony")) {
+       PhoneGap.addResource("telephony");
+
+/**
+ * This class provides access to the telephony features of the device.
+ * @constructor
+ */
+Telephony = function() {
+       
+}
+
+/**
+ * Calls the specifed number.
+ * @param {Integer} number The number to be called.
+ */
+Telephony.prototype.call = function(number) {
+       // not sure why this is here when it does nothing????
+};
+
+PhoneGap.addConstructor(function() {
+    if (typeof navigator.telephony == "undefined") navigator.telephony = new Telephony();
+});
+};if (!PhoneGap.hasResource("network")) {
+       PhoneGap.addResource("network");
+
+// //////////////////////////////////////////////////////////////////
+
+Connection = function() {
+       /*
+        * One of the connection constants below.
+        */
+       this.type = Connection.UNKNOWN;
+
+       /* initialize from the extended DeviceInfo properties */
+    try {      
+               this.type       = DeviceInfo.connection.type;
+    } 
+       catch(e) {
+    }
+};
+
+Connection.UNKNOWN = "unknown"; // Unknown connection type
+Connection.ETHERNET = "ethernet";
+Connection.WIFI = "wifi";
+Connection.CELL_2G = "2g"; // the default for iOS, for any cellular connection
+Connection.CELL_3G = "3g";
+Connection.CELL_4G = "4g";
+Connection.NONE = "none"; // NO connectivity
+
+
+PhoneGap.addConstructor(function() {
+    if (typeof navigator.network == "undefined") navigator.network = {};
+    if (typeof navigator.network.connection == "undefined") navigator.network.connection = new Connection();
+});
+
+};if (!PhoneGap.hasResource("splashscreen")) {
+       PhoneGap.addResource("splashscreen");
+
+/**
+ * This class provides access to the splashscreen
+ */
+SplashScreen = function() {
+};
+
+SplashScreen.prototype.show = function() {
+    PhoneGap.exec(null, null, "com.phonegap.splashscreen", "show", []);
+};
+
+SplashScreen.prototype.hide = function() {
+    PhoneGap.exec(null, null, "com.phonegap.splashscreen", "hide", []);
+};
+
+PhoneGap.addConstructor(function() {
+    if (typeof navigator.splashscreen == "undefined") navigator.splashscreen = new SplashScreen();
+});
+
+};
diff --git a/www/js/view.js b/www/js/view.js
new file mode 100644 (file)
index 0000000..3e0c74d
--- /dev/null
@@ -0,0 +1,496 @@
+/*
+ * This file is part of WolneLektury-Mobile, licensed under GNU Affero GPLv3 or later.
+ * Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+ */
+
+var View = new function() {
+       var self = this;
+       //self.minOffset = 1000;
+       self.categories = {
+                       autor: 'Autorzy', 
+                       rodzaj: 'Rodzaje',
+                       gatunek: 'Gatunki',
+                       epoka: 'Epoki'
+       };
+       self.category_msc = {
+               autor: 'autorze',
+               rodzaj: 'rodzaju',
+               gatunek: 'gatunku',
+               epoka: 'epoce'
+       };
+
+       self.init = function(success, error) {
+               debug('View.init');
+
+               self._searchbox = document.getElementById("searchbox");
+               self._searchinput = document.getElementById("search");
+               self._content = document.getElementById("content");
+               self._back = document.getElementById("back-button");
+
+               self.current = '';
+               self.currentView = '';
+               self.currentPar = '';
+               self.currentTitle = '';
+
+               self.checkNightMode();
+
+               success && success();
+       };
+
+       this.go = function() {
+               document.getElementById("cover").style.display = 'none';
+               self.at_spinner = false;
+               self.enter('');
+       };
+
+
+       this.sanitize = function(text) {
+               return text.replace(/&/g, "&amp;").replace(/</g, "&lt;");
+       };
+
+       this.showSearch = function() {
+               self._searchbox.style.display = "block";
+       };
+
+       this.hideSearch = function() {
+               self._searchbox.style.display = "none";
+       };
+
+       this.spinner = function(text) {
+               if (!text)
+                       text = "Ładowanie";
+               if (self.at_spinner) {
+                       document.getElementById("spinnertext").innerHTML = text;
+               }
+               else {
+                       self._content.innerHTML = "<div class='spinner'><img src='img/spinner.png' /><div id='spinnertext'>" + text +"</div></div>";
+                       self.at_spinner = true;
+               }
+               setOffset(0);
+       };
+
+       this.content = function(text, offset) {
+               debug('content');
+               self.at_spinner = false;
+
+               self._content.innerHTML = '';
+               self._content.innerHTML = text;
+               setOffset(offset);
+       }
+
+       this.enter = function(url, offset) {
+               debug('View.enter: ' + url);
+
+               var view = 'Index';
+               var arg = null;
+
+               if (url.length) {
+                       var slash_index = url.indexOf('/');
+                       if (slash_index != -1) {
+                               view = url.substr(0, slash_index);
+                               arg = url.substr(slash_index + 1);
+                       }
+                       else {
+                               view = url;
+                       }
+               }
+               debug('View.enter: ' + view + ' ' + arg);
+               self.current = url;
+               self.currentView = view;
+               self.currentPar = arg;
+               self['enter' + view](arg, offset);
+               Menu.refresh();
+       }
+       
+       this.enterIndex = function(arg, offset) {
+               debug('enterIndex');            
+               Menu.setInfoButton("ProjectInfo", "O projekcie", true);
+               self.showSearch();
+               self.currentTitle = "Początek";
+               var html = "";
+
+               html += "<div class='buttons'>";
+               html += Links.button('Last', '', 'Ostatnio czytane');
+               html += Links.button('Bookmarks', '', 'Zakładki');
+
+               for (category in self.categories)
+                       html += Links.button('Category', category, self.categories[category], 0);
+               html += "</div>";
+               
+               html += "</div>" +"";
+                               /*"<p id='logo'><img src='img/logo-wl-nq8.png' alt='Wolne Lektury' /><br/>\n" +
+                               "szkolna biblioteka internetowa" +
+                               "</p>";*/
+               self.content(html, offset);
+       };
+       
+       this.enterBook = function(id, offset) {
+               id = parseInt(id);
+               debug('enterBook: ' + id);
+               Menu.setInfoButton("BookInfo", "O utworze", true);
+               self.showSearch();
+
+               Catalogue.withBook(id, function(book) {
+                       self.currentTitle = book.authors + ', ' + book.title;
+
+                       Catalogue.withChildren(id, function(children) {
+                               var html = "<h1><span class='subheader'>";
+                               html += book.authors;
+                               html += "</span>" + book.title + "</h1>\n";
+                               if (book.html_file) {
+                                       html += "<div class='buttons'>" + Links.button('BookText', id, "Czytaj tekst") + "</div>";
+                               }
+                               if (children.length) {
+                                       html += "<div class='buttons'>";
+                                       for (c in children) {
+                                               child = children[c];
+                                               html += Links.bookLink(child);
+                                       }
+                                       html += "</div>";
+                               }
+                               self.content(html, offset);                             
+                       });
+               }, function() {
+                       History.goBack();
+               });
+       };
+       
+       this.enterBookText = function(id, offset) {
+               self.hideSearch();
+               self.spinner("Otwieranie utworu");
+               debug('enterBookText: ' + id);
+               Menu.setInfoButton("BookInfo", "O utworze", true);
+               id = parseInt(id);
+
+               setTimeout("History.addRead("+id+");", 0);
+               
+               FileRepo.withHtml(id, function(data) {
+                       self.content(data, offset);
+               }, function(err) {
+                       alert("Błąd pobierania: nie udało się pobrać treści utworu.");
+                       History.goBack();
+               });
+               Catalogue.withBook(id, function(book) {
+                       self.currentTitle = book.authors + ', ' + book.title;
+               });
+       };
+
+
+       this.enterLast = function(ignored, offset) {
+               debug("enterLast");
+               self.showSearch();
+               Menu.setInfoButton("ProjectInfo", "O projekcie", true);
+               self.currentTitle = 'Ostatnio czytane';
+               var html = "<h1><span class='subheader'>Ostatnio czytane</h1>\n";
+
+               var last_read = History.lastRead();
+               var some_books = false;
+
+               html += "<div class='buttons'>";
+               var add_books = function() {
+                       if (last_read.length) {
+                               var id = last_read.shift();
+                               Catalogue.withBook(id, function(book) {
+                                       html += Links.bookLink(book);
+                                       some_books = true;
+                                       add_books();
+                               }, function() {
+                                       add_books();
+                               });
+                       }
+                       else {
+                               if (!some_books) {
+                                       html += "<p>Nie przeczytano żadnych utworów.</p>";
+                               }
+                               html += "</div>";
+                               self.content(html, offset);
+                       }
+               };
+               add_books();
+       };
+
+
+       this.enterBookmarks = function(ignored, offset) {
+               debug("enterBookmarks");
+               self.showSearch();
+               Menu.setInfoButton("ProjectInfo", "O projekcie", true);
+               self.currentTitle = 'Zakładki';
+               var html = "<h1><span class='subheader'>Zakładki</h1>\n";
+
+               var bookmarks = History.bookmarks();
+               if (!bookmarks.length) {
+                       html += "<p>Nie utworzono żadnych zakładek.</p>";
+                       self.content(html, offset);
+                       return;
+               }
+
+               html += "<div class='buttons bookmarks'>";
+               for (i in bookmarks) {
+                       var bm = bookmarks[i];
+
+                       var text = bm.name;
+                       text += "<div class='sub'>" + bm.title + "</div>";
+                       html += Links.deleteButton(bm.id);
+                       html += Links.button(bm.view, bm.par, text, bm.offset);
+               }
+               html += "</div>";
+               self.content(html, offset);
+       };
+
+       this.onBookmarkChange = function() {
+               // TODO: preserve offset
+               if (self.currentView == 'Bookmarks') {
+                       self.enterBookmarks();
+               }
+       };
+
+       this.enterTag = function(id, offset) {
+               id = parseInt(id);
+               debug('enterTag: ' + id);
+               Menu.setInfoButton("TagInfo", "O...", true);
+               self.showSearch();
+
+               self.spinner("Otwieranie listy utworów");
+
+               Catalogue.withTag(id, function(tag) {
+                       Menu.setInfoButton("TagInfo", "O " + self.category_msc[tag.category], true);
+                       self.currentTitle = tag.category + ': ' + tag.name;
+                       var html = "<h1><span class='subheader upper'>" + tag.category + ': </span>' + tag.name + "</h1>\n";
+                       html += "<div class='buttons'>";
+                       if (tag.books) {
+                               Catalogue.withBooks(tag.books, function(books) {
+                                       for (var i in books) {
+                                               var book = books[i];
+                                               html += Links.bookLink(book);
+                                       }
+                                       html += "</div>";
+                                       self.content(html, offset);
+                               });
+                       }
+               }, function() {
+                       History.goBack();
+               });
+       };
+
+
+       this.enterCategory = function(category, offset) {
+               debug('enterCategory: ' + category);
+               Menu.setInfoButton("ProjectInfo", "O projekcie", true);
+               self.spinner("Otwieranie katalogu");
+               self.showSearch();
+               self.currentTitle = self.categories[category];
+
+               Catalogue.withCategory(category, function(tags) {
+                       var html = "<h1>" + self.categories[category] + "</h1>\n";
+                       html += "<div class='buttons'>";
+                       for (i in tags) {
+                               tag = tags[i];
+                               html += Links.button('Tag', tag.id, tag.name);
+                       }
+                       html += "</div>";
+                       self.content(html, offset);
+               });
+       };
+
+
+       this.enterSearch = function(query, offset) {
+               debug('enterSearch: ' + query);
+               Menu.setInfoButton("ProjectInfo", "O projekcie", true);
+               self.currentTitle = 'Szukaj: ' + query;
+               self.showSearch();
+
+               var html = "<h1><span class='subheader'>Szukana fraza:</span>" + View.sanitize(query) + "</h1>\n";
+
+               if (query.length < 2) {
+                       html += "<p>Szukana fraza musi mieć co najmniej dwa znaki</p>";
+                       self.content(html, offset);
+                       return;
+               }
+
+               Catalogue.withSearch(query, function(results) {
+                       if (results.length == 1) {
+                           var result = results[0];
+                           if (result.view == 'Book' && result.item.html_file) {
+                               self.enter(Links.href('BookText', result.item.id));
+                           }
+                           else {
+                                       self.enter(Links.href(result.view, result.item.id));
+                               }
+                               return;
+                       }
+                       if (results.length == 0) {
+                               html += "<p>Brak wyników wyszukiwania</p>";
+                       }
+                       else {
+                               html += "<div class='buttons'>";
+                               for (var i in results) {
+                                       var result = results[i];
+                                       if (result.view == 'Book')
+                                               html += Links.bookLink(result.item)
+                                       else
+                                               html += Links.button(result.view, result.item.id, result.item.name+"<div class='sub'>"+result.item.category+"</div>");
+                               }
+                               html += "</div>";
+                       }
+                       self.content(html, offset);
+               });
+       };
+
+
+       /* info */
+
+       this.enterProjectInfo = function(arg, offset) {
+               debug('enterProjectInfo');
+               Menu.setInfoButton("ProjectInfo", "O projekcie", false);
+               self.hideSearch();
+               self.currentTitle = "O projekcie";
+
+               var html = "";
+
+               html += '<div class="info">';
+
+
+               html += "<p style='text-align:center'><img src='img/logo-wl.png' /></p>";
+               html += "<p>Biblioteka internetowa Wolne Lektury "+
+" udostępnia w swoich zbiorach lektury szkolne zalecane do użytku przez" + 
+" Ministerstwo Edukacji Narodowej i inne dzieła literatury.</p>";
+
+               html += "<p style='text-align:center'><img src='img/logo-fnp.png' /></p>";
+
+               html += "<img style='float:left;' src='img/procent.png' />" +
+                       "<p style='margin-left: 50px'>" + 
+                       "Przekaż 1% podatku na rozwój Wolnych Lektur.<br/>" +
+                       "Fundacja Nowoczesna Polska<br/>" +
+                       "KRS 0000070056</p>";
+
+               html += "<p>Większość pozycji w bibliotece należy do domeny publicznej "+
+                       "co oznacza, że nie są już chronione majatkowym prawem autorskim, "+
+                       "a więc można je swobodnie wykorzystywać, publikować i rozpowszechniać. "+
+                       "Publikujemy również kilka utworów, które autorzy udostępnili na wolnej licencji "+
+                       "<a href='http://creativecommons.org/licenses/by-sa/3.0/deed.pl'>"+
+                       "Creative Commons Uznanie Autorstwa - Na Tych Samych Warunkach 3.0.PL</a>.</p>";
+
+               html += "<p style='text-align:center'><img src='img/cc-by-sa.png' /></p>";
+
+               html += "<p>Copyright © 2011 Fundacja Nowoczesna Polska. Aplikacja jest wolnym oprogramowaniem "+
+                               "dostępnym na licencji GNU Affero GPL w wersji 3 lub późniejszej.</p>";
+
+               html += "<p>Więcej informacji o projekcie znajduje sie na stronie <a href='http://www.wolnelektury.pl'>http://www.wolnelektury.pl</a>.</p>";
+
+               html += '</div>';
+
+
+               self.content(html, offset);
+       };
+       
+       
+       this.enterBookInfo = function(id, offset) {
+               id = parseInt(id);
+               debug('enterBookInfo: ' + id);
+               Menu.setInfoButton("ProjectInfo", "O projekcie", true);
+               self.hideSearch();
+
+               Catalogue.withBook(id, function(book) {
+                       self.currentTitle = "Informacje o: " + book.title;
+
+                       var html = '<h2>' + book.authors + ', ' + book.title + '</h2>';
+
+                       var url = WL + '/api/book/' + id + '/info.html';
+
+                       var xhr = new XMLHttpRequest();
+                       xhr.open("GET", url);
+                       xhr.onload = function() {
+                               debug('BookInfo: fetched by ajax: ' + url);
+
+                               html += '<div class="info">';
+                               html += xhr.responseText;
+                               html += '</div>';
+
+                               self.content(html, offset);
+                       }
+                       xhr.onerror = function(e) {
+                               self.content("Brak informacji.", offset);
+                       }
+                       xhr.send();
+               }, function() {
+                       History.goBack();
+               });
+       };
+
+
+       this.enterTagInfo = function(id, offset) {
+               id = parseInt(id);
+               debug('enterTagInfo: ' + id);
+               Menu.setInfoButton("ProjectInfo", "O projekcie", true);
+               self.hideSearch();
+
+               Catalogue.withTag(id, function(tag) {
+                       self.currentTitle = "Informacje o " + tag.name;
+                       var html = '<h2>' + tag.name + '</h2>';
+
+                       var url = WL + '/api/tag/' + id + '/info.html';
+
+                       var xhr = new XMLHttpRequest();
+                       xhr.open("GET", url);
+                       xhr.onload = function() {
+                               debug('TagInfo: fetched by ajax: ' + url);
+
+                               html += '<div class="info">';
+                               html += xhr.responseText;
+                               html += '</div>';
+
+                               self.content(html, offset);
+                       }
+                       xhr.onerror = function(e) {
+                               self.content("Brak informacji.", offset);
+                       }
+                       xhr.send();
+               }, function() {
+                       History.goBack();
+               });
+       };
+
+
+       /* search form submit callback */
+       this.search = function() {
+               History.visit('Search/' + self._searchinput.value);
+               self._content.focus()
+               return false;
+       }
+       
+
+       self.getNightMode = function() {
+               night_mode = window.localStorage.getItem('View.night_mode');
+               if (night_mode === undefined)
+                       return false;
+               else
+                       return !!night_mode;
+       };
+
+       self.checkNightMode = function() {
+       night_mode = self.getNightMode();
+               if (night_mode) {
+                       document.body.setAttribute("class", "night-mode");
+               }
+               else {
+                       document.body.setAttribute("class", "");
+               }
+       };
+
+       self.setNightMode = function(night_mode) {
+               night_mode = night_mode ? "1" :  "";
+       window.localStorage.setItem('View.night_mode', night_mode);
+       self.checkNightMode();
+       };
+
+       self.toggleNightMode = function(night_mode) {
+               self.setNightMode(!self.getNightMode());
+       };
+
+       self.showBack = function(show) {
+               if (show)
+                       self._back.style.display = 'inline-block';
+               else
+                       self._back.style.display = 'none';
+       }
+}