Sqlite-Plugin, DB-Copy plugin added und integriert

This commit is contained in:
Carsten Hilmer 2016-01-15 00:59:23 +01:00
parent 337215938a
commit 7c683d7450
243 changed files with 308639 additions and 253 deletions

View File

@ -23,7 +23,15 @@
"ionic-plugin-keyboard", "ionic-plugin-keyboard",
"cordova-plugin-inappbrowser", "cordova-plugin-inappbrowser",
"cordova-plugin-file", "cordova-plugin-file",
"org.apache.cordova.file-transfer" "org.apache.cordova.file-transfer",
{
"locator": "https://github.com/brodysoft/Cordova-SQLitePlugin.git",
"id": "cordova-sqlite-storage"
},
{
"locator": "https://github.com/an-rahulpandey/cordova-plugin-dbcopy.git",
"id": "me.rahul.plugins.sqlDB"
}
], ],
"cordovaPlatforms": [ "cordovaPlatforms": [
"android" "android"

View File

@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="sqlite-connector">
<CLASSES>
<root url="jar://$PROJECT_DIR$/libs/sqlite-connector.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@ -1,7 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="AndroidLogFilters">
<option name="TOOL_WINDOW_CUSTOM_FILTER" value="raataar_wrk.db" />
<option name="TOOL_WINDOW_LOG_LEVEL" value="INFO" />
<option name="TOOL_WINDOW_CONFIGURED_FILTER" value="Show only selected application" />
</component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="e7d7b196-7dbe-4017-ac13-28cca15e160a" name="Default" comment="" /> <list default="true" id="1a60bf2a-41b9-42f3-a21a-2b8a537063de" name="Default" comment="" />
<ignored path="android.iws" /> <ignored path="android.iws" />
<ignored path=".idea/workspace.xml" /> <ignored path=".idea/workspace.xml" />
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" /> <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
@ -19,10 +24,19 @@
<component name="FavoritesManager"> <component name="FavoritesManager">
<favorites_list name="android" /> <favorites_list name="android" />
</component> </component>
<component name="GenerateSignedApkSettings"> <component name="FileEditorManager">
<option name="KEY_STORE_PATH" value="D:\_PROJEKTE_\Android_NAS\android-playstore-stuff\KeyStoreAPK\raataar.jks" /> <leaf>
<option name="KEY_ALIAS" value="wollerosenkaufen" /> <file leaf-file-name="app.js" pinned="false" current-in-tab="true">
<option name="REMEMBER_PASSWORDS" value="true" /> <entry file="file://$PROJECT_DIR$/assets/www/js/app.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.46703297">
<caret line="50" column="48" selection-start-line="50" selection-start-column="48" selection-end-line="50" selection-end-column="48" />
<folding />
</state>
</provider>
</entry>
</file>
</leaf>
</component> </component>
<component name="GradleLocalSettings"> <component name="GradleLocalSettings">
<option name="availableProjects"> <option name="availableProjects">
@ -945,7 +959,7 @@
</option> </option>
<option name="modificationStamps"> <option name="modificationStamps">
<map> <map>
<entry key="C:\Ionic\Ionic\wollerosenkaufen\platforms\android" value="5801013707543" /> <entry key="C:\Ionic\Ionic\git\wolle-rosen-kaufen\platforms\android" value="5805882473891" />
</map> </map>
</option> </option>
<option name="projectBuildClasspath"> <option name="projectBuildClasspath">
@ -960,6 +974,7 @@
<ExternalModuleBuildClasspathPojo> <ExternalModuleBuildClasspathPojo>
<option name="entries"> <option name="entries">
<list> <list>
<option value="$MODULE_DIR$/libs/sqlite-connector.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.android.tools.build/gradle/1.0.0/cc834d833ada2ae95c63eedfc87efcd2d842f3d6/gradle-1.0.0-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.android.tools.build/gradle/1.0.0/cc834d833ada2ae95c63eedfc87efcd2d842f3d6/gradle-1.0.0-sources.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.android.tools.build/gradle/1.0.0/79f4ed9b6582ecec4d7b35aecfa45fbc48dadb96/gradle-1.0.0.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.android.tools.build/gradle/1.0.0/79f4ed9b6582ecec4d7b35aecfa45fbc48dadb96/gradle-1.0.0.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.android.tools.build/builder/1.0.0/5937e1bdf4c807a0c2adf3e1269838238d05da2b/builder-1.0.0-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.android.tools.build/builder/1.0.0/5937e1bdf4c807a0c2adf3e1269838238d05da2b/builder-1.0.0-sources.jar" />
@ -1035,6 +1050,7 @@
<ExternalModuleBuildClasspathPojo> <ExternalModuleBuildClasspathPojo>
<option name="entries"> <option name="entries">
<list> <list>
<option value="$MODULE_DIR$/libs/sqlite-connector.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.android.tools.build/gradle/1.0.0/cc834d833ada2ae95c63eedfc87efcd2d842f3d6/gradle-1.0.0-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.android.tools.build/gradle/1.0.0/cc834d833ada2ae95c63eedfc87efcd2d842f3d6/gradle-1.0.0-sources.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.android.tools.build/gradle/1.0.0/79f4ed9b6582ecec4d7b35aecfa45fbc48dadb96/gradle-1.0.0.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.android.tools.build/gradle/1.0.0/79f4ed9b6582ecec4d7b35aecfa45fbc48dadb96/gradle-1.0.0.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.android.tools.build/builder/1.0.0/5937e1bdf4c807a0c2adf3e1269838238d05da2b/builder-1.0.0-sources.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.android.tools.build/builder/1.0.0/5937e1bdf4c807a0c2adf3e1269838238d05da2b/builder-1.0.0-sources.jar" />
@ -1225,13 +1241,21 @@
<projects_view /> <projects_view />
</option> </option>
</component> </component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/assets/www/js/app.js" />
</list>
</option>
</component>
<component name="NamedScopeManager"> <component name="NamedScopeManager">
<order /> <order />
</component> </component>
<component name="ProjectFrameBounds"> <component name="ProjectFrameBounds">
<option name="x" value="1" /> <option name="x" value="1912" />
<option name="width" value="1678" /> <option name="y" value="-8" />
<option name="height" value="1010" /> <option name="width" value="1696" />
<option name="height" value="1066" />
</component> </component>
<component name="ProjectLevelVcsManager" settingsEditedManually="false"> <component name="ProjectLevelVcsManager" settingsEditedManually="false">
<OptionsSetting value="true" id="Add" /> <OptionsSetting value="true" id="Add" />
@ -1256,6 +1280,8 @@
<sortByType /> <sortByType />
</navigator> </navigator>
<panes> <panes>
<pane id="ProjectPane" />
<pane id="Scratches" />
<pane id="AndroidView"> <pane id="AndroidView">
<subPane> <subPane>
<PATH> <PATH>
@ -1273,23 +1299,68 @@
<option name="myItemId" value="android" /> <option name="myItemId" value="android" />
<option name="myItemType" value="com.android.tools.idea.navigator.nodes.AndroidModuleNode" /> <option name="myItemType" value="com.android.tools.idea.navigator.nodes.AndroidModuleNode" />
</PATH_ELEMENT> </PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="assets" />
<option name="myItemType" value="com.android.tools.idea.navigator.nodes.AndroidSourceTypeNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="www" />
<option name="myItemType" value="com.android.tools.idea.navigator.nodes.AndroidPsiDirectoryNode" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="android" />
<option name="myItemType" value="com.android.tools.idea.navigator.nodes.AndroidViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="android" />
<option name="myItemType" value="com.android.tools.idea.navigator.nodes.AndroidModuleNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="assets" />
<option name="myItemType" value="com.android.tools.idea.navigator.nodes.AndroidSourceTypeNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="www" />
<option name="myItemType" value="com.android.tools.idea.navigator.nodes.AndroidPsiDirectoryNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="js" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="android" />
<option name="myItemType" value="com.android.tools.idea.navigator.nodes.AndroidViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="android" />
<option name="myItemType" value="com.android.tools.idea.navigator.nodes.AndroidModuleNode" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="android" />
<option name="myItemType" value="com.android.tools.idea.navigator.nodes.AndroidViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="CordovaLib" />
<option name="myItemType" value="com.android.tools.idea.navigator.nodes.AndroidModuleNode" />
</PATH_ELEMENT>
</PATH> </PATH>
</subPane> </subPane>
</pane> </pane>
<pane id="Scratches" />
<pane id="PackagesPane" />
<pane id="Scope" /> <pane id="Scope" />
<pane id="ProjectPane" /> <pane id="PackagesPane" />
</panes> </panes>
</component> </component>
<component name="PropertiesComponent"> <component name="PropertiesComponent">
<property name="android.sdk.path" value="C:/Program Files (x86)/Android/android-sdk" /> <property name="android.sdk.path" value="C:/Program Files (x86)/Android/android-sdk" />
<property name="recentsLimit" value="5" /> <property name="recentsLimit" value="5" />
<property name="last_opened_file_path" value="D:/_PROJEKTE_/Android_NAS/android-playstore-stuff/KeyStoreAPK/raataar.jks" /> <property name="ANDROID_EXTENDED_DEVICE_CHOOSER_SERIALS" value="CB5A1UMZYM" />
<property name="ExportApk.ApkPath" value="C:\Ionic\Ionic\wollerosenkaufen\platforms\android" /> <property name="ANDROID_EXTENDED_DEVICE_CHOOSER_AVD" value="MyAvd0" />
<property name="ExportApk.Flavors" value="" />
<property name="ExportApk.BuildType" value="release" />
<property name="FullScreen" value="false" />
</component> </component>
<component name="RunManager" selected="Android Application.android"> <component name="RunManager" selected="Android Application.android">
<configuration default="true" type="AndroidRunConfigurationType" factoryName="Android Application"> <configuration default="true" type="AndroidRunConfigurationType" factoryName="Android Application">
@ -1321,6 +1392,36 @@
<option name="CLOUD_DEVICE_SERIAL_NUMBER" value="" /> <option name="CLOUD_DEVICE_SERIAL_NUMBER" value="" />
<method /> <method />
</configuration> </configuration>
<configuration default="true" type="AndroidTestRunConfigurationType" factoryName="Android Tests">
<module name="" />
<option name="TESTING_TYPE" value="0" />
<option name="INSTRUMENTATION_RUNNER_CLASS" value="" />
<option name="METHOD_NAME" value="" />
<option name="CLASS_NAME" value="" />
<option name="PACKAGE_NAME" value="" />
<option name="TARGET_SELECTION_MODE" value="EMULATOR" />
<option name="USE_LAST_SELECTED_DEVICE" value="false" />
<option name="PREFERRED_AVD" value="" />
<option name="USE_COMMAND_LINE" value="true" />
<option name="COMMAND_LINE" value="" />
<option name="WIPE_USER_DATA" value="false" />
<option name="DISABLE_BOOT_ANIMATION" value="false" />
<option name="NETWORK_SPEED" value="full" />
<option name="NETWORK_LATENCY" value="none" />
<option name="CLEAR_LOGCAT" value="false" />
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="true" />
<option name="FILTER_LOGCAT_AUTOMATICALLY" value="true" />
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="0" />
<option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
<option name="SELECTED_CLOUD_DEVICE_CONFIGURATION_ID" value="0" />
<option name="SELECTED_CLOUD_DEVICE_PROJECT_ID" value="" />
<option name="IS_VALID_CLOUD_MATRIX_SELECTION" value="false" />
<option name="INVALID_CLOUD_MATRIX_SELECTION_ERROR" value="" />
<option name="IS_VALID_CLOUD_DEVICE_SELECTION" value="false" />
<option name="INVALID_CLOUD_DEVICE_SELECTION_ERROR" value="" />
<option name="CLOUD_DEVICE_SERIAL_NUMBER" value="" />
<method />
</configuration>
<configuration default="true" type="Application" factoryName="Application"> <configuration default="true" type="Application" factoryName="Application">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" /> <option name="MAIN_CLASS_NAME" />
@ -1357,6 +1458,11 @@
<patterns /> <patterns />
<method /> <method />
</configuration> </configuration>
<configuration default="true" type="JarApplication" factoryName="JAR Application">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<envs />
<method />
</configuration>
<configuration default="true" type="Remote" factoryName="Remote"> <configuration default="true" type="Remote" factoryName="Remote">
<option name="USE_SOCKET_TRANSPORT" value="true" /> <option name="USE_SOCKET_TRANSPORT" value="true" />
<option name="SERVER_MODE" value="false" /> <option name="SERVER_MODE" value="false" />
@ -1439,58 +1545,47 @@
</configuration> </configuration>
</component> </component>
<component name="ShelveChangesManager" show_recycled="false" /> <component name="ShelveChangesManager" show_recycled="false" />
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager"> <component name="TaskManager">
<task active="true" id="Default" summary="Default task"> <task active="true" id="Default" summary="Default task">
<changelist id="e7d7b196-7dbe-4017-ac13-28cca15e160a" name="Default" comment="" /> <changelist id="1a60bf2a-41b9-42f3-a21a-2b8a537063de" name="Default" comment="" />
<created>1450381598364</created> <created>1452811340653</created>
<option name="number" value="Default" /> <option name="number" value="Default" />
<updated>1450381598364</updated> <updated>1452811340653</updated>
</task> </task>
<servers /> <servers />
</component> </component>
<component name="ToolWindowManager"> <component name="ToolWindowManager">
<frame x="1" y="0" width="1678" height="1010" extended-state="1" /> <frame x="1912" y="-8" width="1696" height="1066" extended-state="6" />
<editor active="false" /> <editor active="true" />
<layout> <layout>
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
<window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" /> <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32941177" sideWeight="0.49573562" order="-1" side_tool="false" content_ui="tabs" />
<window_info id="Palette&#9;" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" /> <window_info id="Palette&#9;" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
<window_info id="Build Variants" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="true" content_ui="tabs" /> <window_info id="Build Variants" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="true" content_ui="tabs" />
<window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="true" content_ui="tabs" /> <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.36923078" sideWeight="0.34963325" order="-1" side_tool="true" content_ui="tabs" />
<window_info id="Application Servers" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" /> <window_info id="Application Servers" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
<window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" /> <window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" /> <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
<window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" /> <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32941177" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32941177" sideWeight="0.4978678" order="-1" side_tool="false" content_ui="tabs" />
<window_info id="Captures" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" /> <window_info id="Captures" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
<window_info id="Gradle Console" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="true" content_ui="tabs" /> <window_info id="Gradle Console" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32941177" sideWeight="0.5042644" order="-1" side_tool="true" content_ui="tabs" />
<window_info id="Android" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" /> <window_info id="Android" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.36923078" sideWeight="0.6503667" order="-1" side_tool="false" content_ui="tabs" />
<window_info id="Designer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" /> <window_info id="Designer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
<window_info id="Project" active="true" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.24969098" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" /> <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.28667483" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
<window_info id="Gradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" /> <window_info id="Gradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="true" content_ui="tabs" /> <window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="true" content_ui="tabs" />
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> <window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" /> <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="SLIDING" type="SLIDING" visible="false" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="SLIDING" type="SLIDING" visible="false" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
</layout> </layout>
</component> </component>
<component name="Vcs.Log.UiProperties">
<option name="RECENTLY_FILTERED_USER_GROUPS">
<collection />
</option>
<option name="RECENTLY_FILTERED_BRANCH_GROUPS">
<collection />
</option>
</component>
<component name="VcsContentAnnotationSettings"> <component name="VcsContentAnnotationSettings">
<option name="myLimit" value="2678400000" /> <option name="myLimit" value="2678400000" />
</component> </component>
@ -1498,4 +1593,14 @@
<breakpoint-manager /> <breakpoint-manager />
<watches-manager /> <watches-manager />
</component> </component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/assets/www/js/app.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.46703297">
<caret line="50" column="48" selection-start-line="50" selection-start-column="48" selection-end-line="50" selection-end-column="48" />
<folding />
</state>
</provider>
</entry>
</component>
</project> </project>

View File

@ -1,2 +1,2 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<merger version="3"><dataSet config="debug"><source path="C:\Ionic\Ionic\wollerosenkaufen\platforms\android\CordovaLib\build\intermediates\bundles\debug\assets"/></dataSet><dataSet config="main"><source path="C:\Ionic\Ionic\wollerosenkaufen\platforms\android\CordovaLib\src\androidTest\assets"/></dataSet></merger> <merger version="3"><dataSet config="debug"><source path="C:\Ionic\Ionic\git\wolle-rosen-kaufen\platforms\android\CordovaLib\build\intermediates\bundles\debug\assets"/></dataSet><dataSet config="main"><source path="C:\Ionic\Ionic\git\wolle-rosen-kaufen\platforms\android\CordovaLib\src\androidTest\assets"/></dataSet></merger>

View File

@ -1,2 +1,2 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<merger version="3"><dataSet config="debug"><source path="C:\Ionic\Ionic\wollerosenkaufen\platforms\android\CordovaLib\build\intermediates\bundles\debug\res"/></dataSet><dataSet config="main"><source path="C:\Ionic\Ionic\wollerosenkaufen\platforms\android\CordovaLib\src\androidTest\res"/><source path="C:\Ionic\Ionic\wollerosenkaufen\platforms\android\CordovaLib\build\generated\res\rs\test\debug"/><source path="C:\Ionic\Ionic\wollerosenkaufen\platforms\android\CordovaLib\build\generated\res\generated\test\debug"/></dataSet><mergedItems/></merger> <merger version="3"><dataSet config="debug"><source path="C:\Ionic\Ionic\git\wolle-rosen-kaufen\platforms\android\CordovaLib\build\intermediates\bundles\debug\res"/></dataSet><dataSet config="main"><source path="C:\Ionic\Ionic\git\wolle-rosen-kaufen\platforms\android\CordovaLib\src\androidTest\res"/><source path="C:\Ionic\Ionic\git\wolle-rosen-kaufen\platforms\android\CordovaLib\build\generated\res\rs\test\debug"/><source path="C:\Ionic\Ionic\git\wolle-rosen-kaufen\platforms\android\CordovaLib\build\generated\res\generated\test\debug"/></dataSet><mergedItems/></merger>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.apache.cordova.test">
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="14" />
<application>
<uses-library android:name="android.test.runner" />
</application>
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="org.apache.cordova.test"
android:handleProfiling="false"
android:functionalTest="false"
android:label="Tests for org.apache.cordova.test"/>
</manifest>

View File

@ -83,6 +83,7 @@
</content> </content>
<orderEntry type="jdk" jdkName="Android API 22 Platform" jdkType="Android SDK" /> <orderEntry type="jdk" jdkName="Android API 22 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="sqlite-connector" level="project" />
<orderEntry type="module" module-name="CordovaLib" exported="" /> <orderEntry type="module" module-name="CordovaLib" exported="" />
</component> </component>
</module> </module>

View File

@ -11,6 +11,10 @@
{ {
"xml": "<uses-permission android:name=\"com.android.vending.BILLING\" />", "xml": "<uses-permission android:name=\"com.android.vending.BILLING\" />",
"count": 1 "count": 1
},
{
"xml": "<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />",
"count": 1
} }
], ],
"/*": [ "/*": [
@ -59,6 +63,14 @@
{ {
"xml": "<feature name=\"FileTransfer\"><param name=\"android-package\" value=\"org.apache.cordova.filetransfer.FileTransfer\" /></feature>", "xml": "<feature name=\"FileTransfer\"><param name=\"android-package\" value=\"org.apache.cordova.filetransfer.FileTransfer\" /></feature>",
"count": 1 "count": 1
},
{
"xml": "<feature name=\"SQLitePlugin\"><param name=\"android-package\" value=\"io.liteglue.SQLitePlugin\" /></feature>",
"count": 1
},
{
"xml": "<feature name=\"sqlDB\"><param name=\"android-package\" value=\"me.rahul.plugins.sqlDB.sqlDB\" /></feature>",
"count": 1
} }
] ]
} }
@ -116,6 +128,12 @@
}, },
"cordova-plugin-file-transfer": { "cordova-plugin-file-transfer": {
"PACKAGE_NAME": "com.raataar.wolle_rosen_kaufen" "PACKAGE_NAME": "com.raataar.wolle_rosen_kaufen"
},
"cordova-sqlite-storage": {
"PACKAGE_NAME": "com.raataar.wolle_rosen_kaufen"
},
"me.rahul.plugins.sqlDB": {
"PACKAGE_NAME": "com.raataar.wolle_rosen_kaufen"
} }
}, },
"dependent_plugins": {}, "dependent_plugins": {},
@ -358,6 +376,22 @@
"clobbers": [ "clobbers": [
"window.FileTransfer" "window.FileTransfer"
] ]
},
{
"file": "plugins/cordova-sqlite-storage/www/SQLitePlugin.js",
"id": "cordova-sqlite-storage.SQLitePlugin",
"pluginId": "cordova-sqlite-storage",
"clobbers": [
"SQLitePlugin"
]
},
{
"file": "plugins/me.rahul.plugins.sqlDB/www/sqlDB.js",
"id": "me.rahul.plugins.sqlDB.sqlDB",
"pluginId": "me.rahul.plugins.sqlDB",
"clobbers": [
"window.plugins.sqlDB"
]
} }
], ],
"plugin_metadata": { "plugin_metadata": {
@ -370,6 +404,8 @@
"ionic-plugin-keyboard": "1.0.8", "ionic-plugin-keyboard": "1.0.8",
"cordova-plugin-inappbrowser": "1.1.1", "cordova-plugin-inappbrowser": "1.1.1",
"cordova-plugin-file": "3.0.0", "cordova-plugin-file": "3.0.0",
"cordova-plugin-file-transfer": "1.4.0" "cordova-plugin-file-transfer": "1.4.0",
"cordova-sqlite-storage": "0.7.15-pre",
"me.rahul.plugins.sqlDB": "1.0.3"
} }
} }

View File

@ -238,6 +238,22 @@ module.exports = [
"clobbers": [ "clobbers": [
"window.FileTransfer" "window.FileTransfer"
] ]
},
{
"file": "plugins/cordova-sqlite-storage/www/SQLitePlugin.js",
"id": "cordova-sqlite-storage.SQLitePlugin",
"pluginId": "cordova-sqlite-storage",
"clobbers": [
"SQLitePlugin"
]
},
{
"file": "plugins/me.rahul.plugins.sqlDB/www/sqlDB.js",
"id": "me.rahul.plugins.sqlDB.sqlDB",
"pluginId": "me.rahul.plugins.sqlDB",
"clobbers": [
"window.plugins.sqlDB"
]
} }
]; ];
module.exports.metadata = module.exports.metadata =
@ -252,7 +268,9 @@ module.exports.metadata =
"ionic-plugin-keyboard": "1.0.8", "ionic-plugin-keyboard": "1.0.8",
"cordova-plugin-inappbrowser": "1.1.1", "cordova-plugin-inappbrowser": "1.1.1",
"cordova-plugin-file": "3.0.0", "cordova-plugin-file": "3.0.0",
"cordova-plugin-file-transfer": "1.4.0" "cordova-plugin-file-transfer": "1.4.0",
"cordova-sqlite-storage": "0.7.15-pre",
"me.rahul.plugins.sqlDB": "1.0.3"
} }
// BOTTOM OF METADATA // BOTTOM OF METADATA
}); });

View File

@ -3,6 +3,7 @@
font-family: 'Lobster', cursive; font-family: 'Lobster', cursive;
} }
.lobsterMiddle{ .lobsterMiddle{
font-family: 'Lobster', cursive; font-family: 'Lobster', cursive;
font-size: 1.2em; font-size: 1.2em;
@ -22,35 +23,45 @@
.item-divider{ .item-divider{
background-image: linear-gradient( background-image: linear-gradient(
hsla(340, 100%, 35%, 1) 20%, hsla(0, 94%, 33%, 1) 20%,
hsl(340,100%,54%) 90% hsl(0,94%,54%) 90%
); );
color:white; color:white;
} }
.tabs-addddssertive > .tabs{ .tabs-addddssertive > .tabs{
background-image: linear-gradient( background-image: linear-gradient(
hsla(340, 100%, 35%, 1) 20%, hsla(0, 94%, 33%, 1) 20%,
hsl(340,100%,54%) 90% hsl(0,94%,54%) 90%
) !important; ) !important;
} }
.tabs-assertive > .tabs{ .tabs-assertive > .tabs{
background-color: #BD0340 !important; background-color: #A50505 !important;
} }
.bar-custom{ .bar-custom{
background-image: linear-gradient( background-image: linear-gradient(
hsla(340, 100%, 35%, 1) 20%, hsla(0, 94%, 33%, 1) 20%,
hsl(340,100%,54%) 90% hsl(0,94%,54%) 90%
) !important; ) !important;
font-family: 'Bubblegum Sans', cursive;
} }
.button-custom{ .button-custom{
background-color: #C60545; font-family: 'Bubblegum Sans', cursive;
text-decoration: none; background-color: #A50505;
border: 1px solid #C60545; text-decoration: none;
border-color: #F9135F #F9135F #F9135F #F9135F; border: 1px solid #A50505;
color: #fff color: #fff
} }
.oleo{
font-family: 'Bubblegum Sans', cursive;
font-size: 1.3em !important;
}

View File

@ -8,6 +8,7 @@
<link href="lib/ionic/css/ionic.css" rel="stylesheet"> <link href="lib/ionic/css/ionic.css" rel="stylesheet">
<link href='https://fonts.googleapis.com/css?family=Lobster' rel='stylesheet' type='text/css'> <link href='https://fonts.googleapis.com/css?family=Lobster' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Oswald:400,300,700' rel='stylesheet' type='text/css'> <link href='https://fonts.googleapis.com/css?family=Oswald:400,300,700' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Bubblegum+Sans' rel='stylesheet' type='text/css'>
<link href="css/style.css" rel="stylesheet"> <link href="css/style.css" rel="stylesheet">
<!-- IF using Sass (run gulp sass first), then uncomment below and remove the CSS includes above <!-- IF using Sass (run gulp sass first), then uncomment below and remove the CSS includes above
@ -16,7 +17,7 @@
<!-- ionic/angularjs js --> <!-- ionic/angularjs js -->
<script src="lib/ionic/js/ionic.bundle.js"></script> <script src="lib/ionic/js/ionic.bundle.js"></script>
<script src="js/ng-cordova.min.js"></script>
<!-- cordova script (this will be a 404 during development) --> <!-- cordova script (this will be a 404 during development) -->
<script src="cordova.js"></script> <script src="cordova.js"></script>

View File

@ -5,21 +5,55 @@
// the 2nd parameter is an array of 'requires' // the 2nd parameter is an array of 'requires'
// 'starter.services' is found in services.js // 'starter.services' is found in services.js
// 'starter.controllers' is found in controllers.js // 'starter.controllers' is found in controllers.js
angular.module('starter', ['ionic', 'starter.controllers', 'starter.services']) var db;
.run(function($ionicPlatform, $ionicPopup) { angular.module('starter', ['ionic', 'ngCordova', 'starter.controllers', 'starter.services'])
.run(function($ionicPlatform, $window, $ionicHistory, $database, $ionicPopup, $state, $localstorage, $rootScope) {
$ionicPlatform.ready(function() { $ionicPlatform.ready(function() {
// Hide the accessory bar by default (remove this to show the accessory bar above the keyboard // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
// for form inputs) // for form inputs)
$ionicPlatform.registerBackButtonAction(function(event) {
// Handle Android back button to avoid the application exits accidentaly
if ($state.current.name=="tab.dash") {
$ionicPopup.confirm({
title: 'System-Hinweis',
template: 'Möchten Sie die App beenden?'
}).then(function(res) {
if (res) {
ionic.Platform.exitApp();
}
});
} else {
$ionicHistory.clearCache();
$ionicHistory.nextViewOptions({
historyRoot: true
});
$state.go('tab.dash');
}
}, 100);
if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) { if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true); cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
cordova.plugins.Keyboard.disableScroll(true); cordova.plugins.Keyboard.disableScroll(true);
} }
if (window.StatusBar) {
// org.apache.cordova.statusbar required // Mandatory for InAppBrowser plugin
StatusBar.styleDefault(); if(window.cordova){
window.open = cordova.InAppBrowser.open;
} }
// Copy the populated database to mobile device destination
//if (window.sqlitePlugin && window.cordova) {
if (window.sqlitePlugin && window.cordova) {
window.plugins.sqlDB.copy("raataar_wrk.db", function (e) {
console.log(e);
});
}
function successHandler (result) { function successHandler (result) {
var strResult = ""; var strResult = "";
@ -39,7 +73,16 @@ angular.module('starter', ['ionic', 'starter.controllers', 'starter.services'])
if((window.device && device.platform == "Android") && typeof inappbilling !== "undefined") { if((window.device && device.platform == "Android") && typeof inappbilling !== "undefined") {
inappbilling.init(successHandler, errorHandler, {showLog:true}); inappbilling.init(successHandler, errorHandler, {showLog:true});
} }
// Initialize database through $database service
db = $database.initDB();
if (window.StatusBar) {
// org.apache.cordova.statusbar required
StatusBar.styleDefault();
}
}); });
}) })

View File

@ -1,6 +1,6 @@
angular.module('starter.controllers', []) angular.module('starter.controllers', [])
.controller('DashCtrl', function($scope,$ionicPopup,$http, $localstorage) { .controller('DashCtrl', function($scope,$ionicPopup,$http, $database) {
$scope.preis=""; $scope.preis="";
$scope.name=""; $scope.name="";
@ -8,7 +8,9 @@ angular.module('starter.controllers', [])
$scope.items=[]; $scope.items=[];
$scope.item=[]; $scope.item=[];
$scope.items=$localstorage.getObject('items'); //db = Database.getDb();
//$scope.items=$localstorage.getObject('items');
$scope.validate = function() { $scope.validate = function() {
@ -22,16 +24,30 @@ angular.module('starter.controllers', [])
}; };
$scope.loaddata = function() { $scope.loaddata = function() {
$scope.items=$localstorage.getObject('items'); $scope.items=[];
alert(JSON.stringify($scope.items)); $database.getAllBuys().then(function (result) {
if(result.length>0){
alert("daten da");
for(i=0;i<result.length;i++){
$scope.items.push(result[i]);
alert(i);
}
} else {
$scope.items=[];
}
});
}; };
$scope.savedata = function() { $scope.savedata = function() {
$scope.item={name: $scope.name, freitext: $scope.freitext, rosen: $scope.preis, bild: "test"}; $scope.item={name: $scope.name, bild: "test"};
alert(JSON.stringify($scope.item));
$scope.items.push($scope.item); $scope.items.push($scope.item);
$localstorage.setObject('items',$scope.items); $database.setBuys($scope.name,"test");
}; };
function successBuyHandler (result) { function successBuyHandler (result) {
@ -78,14 +94,22 @@ angular.module('starter.controllers', [])
}) })
.controller('BuysCtrl', function($scope, $localstorage,$location) { .controller('BuysCtrl', function($scope, $database,$location) {
$scope.items=[]; $scope.items=[];
$scope.items=$localstorage.getObject('items'); //$scope.items=$localstorage.getObject('items');
$scope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) { $scope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
if ($location.path() == "/tab/buys") { if ($location.path() == "/tab/buys") {
$scope.items=$localstorage.getObject('items'); $database.getAllBuys().then(function (result) {
} if(result.length>0){
for(i=0;i<result.length;i++){
$scope.items.push(result[i]);
}
} else {
$scope.items=[];
}
});
}
}); });

File diff suppressed because one or more lines are too long

View File

@ -1,19 +1,94 @@
angular.module('starter.services', []) angular.module('starter.services', [])
.factory('$localstorage', ['$window', function($window) { .factory('$localstorage', ['$window', function($window) {
return { return {
set: function(key, value) { set: function(key, value) {
$window.localStorage[key] = value; $window.localStorage[key] = value;
}, },
get: function(key, defaultValue) { get: function(key, defaultValue) {
return $window.localStorage[key] || defaultValue; return $window.localStorage[key] || defaultValue;
}, },
setObject: function(key, value) { setObject: function(key, value) {
$window.localStorage[key] = JSON.stringify(value); $window.localStorage[key] = angular.toJson(value);
}, },
getObject: function(key) { getObject: function(key) {
return JSON.parse($window.localStorage[key] || '{}'); return JSON.parse($window.localStorage[key] || '[]');
},
destroy: function(key) {
$window.localStorage.removeItem(key);
},
log: function(key, defaultValue) {
console.log($window.localStorage[key] || defaultValue);
},
logObject: function(key) {
console.log(JSON.stringify($window.localStorage[key] || '{}'));
}
};
}])
.factory('$database',['$cordovaSQLite', function($cordovaSQLite) {
var self = this;
self.db = null;
var query;
//this updateTable function is for ilustrative use here, it can be changed to manage all queries indeed.
updateTable=function (table, column, value, condition) {
if(!condition){
query = "UPDATE " + table + " SET " + column + " = " + value;
} else{
query = "UPDATE " + table + " SET " + column + " = " + value + " WHERE " + condition;
} }
} return $cordovaSQLite.execute(self.db,query,[]).
}]); then(function(result) {
console.log(query);
query=null;
return result;
},function(error) {
console.error(error);
query=null;
return error;
});
};
return {
initDB: function() {
if(window.cordova){
//self.db = sqlitePlugin.openDatabase({name: "data.db", location: 2, createFromLocation: 1});
self.db = $cordovaSQLite.openDB("raataar_wrk.db");
} else {
self.db = window.openDatabase('raataar_wrk.db','1','my',800*1024); // only available when WebSQL is available in Browser
}
console.log('Database opened');
return self.db;
},
setBuys: function(itemName,itemBild) {
query="INSERT INTO buys (Name, Bild) VALUES (?,?);";
return $cordovaSQLite.execute(self.db,query,[itemName,itemBild]).
then(function(result) {
console.log("Gespeichert");
query=null;
}, function(error) {
console.error(error);
});
},
getAllBuys: function(){
var ArrayQ=[];
query="SELECT * FROM buys order by id ASC";
return $cordovaSQLite.execute(self.db,query).
then(function(result) {
for(j=0;j<result.rows.length;j++){
var List={};
//console.log(result.rows.item(j));
List.name=result.rows.item(j).Name;
List.bild=result.rows.item(j).Bild;
ArrayQ.push(List);
}
//console.log(ArrayQ);
return ArrayQ;
},function(e){
console.error(error);
return error;
});
}
};
}]);

View File

@ -1,4 +1,4 @@
@charset "UTF-8"; @charset "UTF-8";
/*! /*!
* Copyright 2014 Drifty Co. * Copyright 2014 Drifty Co.
* http://drifty.com/ * http://drifty.com/

View File

@ -0,0 +1,576 @@
cordova.define("cordova-sqlite-storage.SQLitePlugin", function(require, exports, module) { (function() {
var DB_STATE_INIT, DB_STATE_OPEN, READ_ONLY_REGEX, SQLiteFactory, SQLitePlugin, SQLitePluginTransaction, argsArray, dblocations, newSQLError, nextTick, root, txLocks;
root = this;
READ_ONLY_REGEX = /^\s*(?:drop|delete|insert|update|create)\s/i;
DB_STATE_INIT = "INIT";
DB_STATE_OPEN = "OPEN";
txLocks = {};
newSQLError = function(error, code) {
var sqlError;
sqlError = error;
if (!code) {
code = 0;
}
if (!sqlError) {
sqlError = new Error("a plugin had an error but provided no response");
sqlError.code = code;
}
if (typeof sqlError === "string") {
sqlError = new Error(error);
sqlError.code = code;
}
if (!sqlError.code && sqlError.message) {
sqlError.code = code;
}
if (!sqlError.code && !sqlError.message) {
sqlError = new Error("an unknown error was returned: " + JSON.stringify(sqlError));
sqlError.code = code;
}
return sqlError;
};
nextTick = window.setImmediate || function(fun) {
window.setTimeout(fun, 0);
};
/*
Utility that avoids leaking the arguments object. See
https://www.npmjs.org/package/argsarray
*/
argsArray = function(fun) {
return function() {
var args, i, len;
len = arguments.length;
if (len) {
args = [];
i = -1;
while (++i < len) {
args[i] = arguments[i];
}
return fun.call(this, args);
} else {
return fun.call(this, []);
}
};
};
SQLitePlugin = function(openargs, openSuccess, openError) {
var dbname;
if (!(openargs && openargs['name'])) {
throw newSQLError("Cannot create a SQLitePlugin db instance without a db name");
}
dbname = openargs.name;
if (typeof dbname !== 'string') {
throw newSQLError('sqlite plugin database name must be a string');
}
this.openargs = openargs;
this.dbname = dbname;
this.openSuccess = openSuccess;
this.openError = openError;
this.openSuccess || (this.openSuccess = function() {
console.log("DB opened: " + dbname);
});
this.openError || (this.openError = function(e) {
console.log(e.message);
});
this.open(this.openSuccess, this.openError);
};
SQLitePlugin.prototype.databaseFeatures = {
isSQLitePluginDatabase: true
};
SQLitePlugin.prototype.openDBs = {};
SQLitePlugin.prototype.addTransaction = function(t) {
if (!txLocks[this.dbname]) {
txLocks[this.dbname] = {
queue: [],
inProgress: false
};
}
txLocks[this.dbname].queue.push(t);
if (this.dbname in this.openDBs && this.openDBs[this.dbname] !== DB_STATE_INIT) {
this.startNextTransaction();
} else {
if (this.dbname in this.openDBs) {
console.log('new transaction is waiting for open operation');
} else {
console.log('database is closed, new transaction is [stuck] waiting until db is opened again!');
}
}
};
SQLitePlugin.prototype.transaction = function(fn, error, success) {
if (!this.openDBs[this.dbname]) {
error(newSQLError('database not open'));
return;
}
this.addTransaction(new SQLitePluginTransaction(this, fn, error, success, true, false));
};
SQLitePlugin.prototype.readTransaction = function(fn, error, success) {
if (!this.openDBs[this.dbname]) {
error(newSQLError('database not open'));
return;
}
this.addTransaction(new SQLitePluginTransaction(this, fn, error, success, false, true));
};
SQLitePlugin.prototype.startNextTransaction = function() {
var self;
self = this;
nextTick((function(_this) {
return function() {
var txLock;
if (!(_this.dbname in _this.openDBs) || _this.openDBs[_this.dbname] !== DB_STATE_OPEN) {
console.log('cannot start next transaction: database not open');
return;
}
txLock = txLocks[self.dbname];
if (!txLock) {
console.log('cannot start next transaction: database connection is lost');
return;
} else if (txLock.queue.length > 0 && !txLock.inProgress) {
txLock.inProgress = true;
txLock.queue.shift().start();
}
};
})(this));
};
SQLitePlugin.prototype.abortAllPendingTransactions = function() {
var j, len1, ref, tx, txLock;
txLock = txLocks[this.dbname];
if (!!txLock && txLock.queue.length > 0) {
ref = txLock.queue;
for (j = 0, len1 = ref.length; j < len1; j++) {
tx = ref[j];
tx.abortFromQ(newSQLError('Invalid database handle'));
}
txLock.queue = [];
txLock.inProgress = false;
}
};
SQLitePlugin.prototype.open = function(success, error) {
var openerrorcb, opensuccesscb;
if (this.dbname in this.openDBs) {
console.log('database already open: ' + this.dbname);
nextTick((function(_this) {
return function() {
success(_this);
};
})(this));
} else {
console.log('OPEN database: ' + this.dbname);
opensuccesscb = (function(_this) {
return function() {
var txLock;
if (!_this.openDBs[_this.dbname]) {
console.log('database was closed during open operation');
}
if (_this.dbname in _this.openDBs) {
_this.openDBs[_this.dbname] = DB_STATE_OPEN;
}
if (!!success) {
success(_this);
}
txLock = txLocks[_this.dbname];
if (!!txLock && txLock.queue.length > 0 && !txLock.inProgress) {
_this.startNextTransaction();
}
};
})(this);
openerrorcb = (function(_this) {
return function() {
console.log('OPEN database: ' + _this.dbname + ' failed, aborting any pending transactions');
if (!!error) {
error(newSQLError('Could not open database'));
}
delete _this.openDBs[_this.dbname];
_this.abortAllPendingTransactions();
};
})(this);
this.openDBs[this.dbname] = DB_STATE_INIT;
cordova.exec(opensuccesscb, openerrorcb, "SQLitePlugin", "open", [this.openargs]);
}
};
SQLitePlugin.prototype.close = function(success, error) {
if (this.dbname in this.openDBs) {
if (txLocks[this.dbname] && txLocks[this.dbname].inProgress) {
console.log('cannot close: transaction is in progress');
error(newSQLError('database cannot be closed while a transaction is in progress'));
return;
}
console.log('CLOSE database: ' + this.dbname);
delete this.openDBs[this.dbname];
if (txLocks[this.dbname]) {
console.log('closing db with transaction queue length: ' + txLocks[this.dbname].queue.length);
} else {
console.log('closing db with no transaction lock state');
}
cordova.exec(success, error, "SQLitePlugin", "close", [
{
path: this.dbname
}
]);
} else {
console.log('cannot close: database is not open');
if (error) {
nextTick(function() {
return error();
});
}
}
};
SQLitePlugin.prototype.executeSql = function(statement, params, success, error) {
var myerror, myfn, mysuccess;
mysuccess = function(t, r) {
if (!!success) {
return success(r);
}
};
myerror = function(t, e) {
if (!!error) {
return error(e);
}
};
myfn = function(tx) {
tx.addStatement(statement, params, mysuccess, myerror);
};
this.addTransaction(new SQLitePluginTransaction(this, myfn, null, null, false, false));
};
SQLitePluginTransaction = function(db, fn, error, success, txlock, readOnly) {
if (typeof fn !== "function") {
/*
This is consistent with the implementation in Chrome -- it
throws if you pass anything other than a function. This also
prevents us from stalling our txQueue if somebody passes a
false value for fn.
*/
throw newSQLError("transaction expected a function");
}
this.db = db;
this.fn = fn;
this.error = error;
this.success = success;
this.txlock = txlock;
this.readOnly = readOnly;
this.executes = [];
if (txlock) {
this.addStatement("BEGIN", [], null, function(tx, err) {
throw newSQLError("unable to begin transaction: " + err.message, err.code);
});
}
};
SQLitePluginTransaction.prototype.start = function() {
var err, error1;
try {
this.fn(this);
this.run();
} catch (error1) {
err = error1;
txLocks[this.db.dbname].inProgress = false;
this.db.startNextTransaction();
if (this.error) {
this.error(newSQLError(err));
}
}
};
SQLitePluginTransaction.prototype.executeSql = function(sql, values, success, error) {
if (this.finalized) {
throw {
message: 'InvalidStateError: DOM Exception 11: This transaction is already finalized. Transactions are committed after its success or failure handlers are called. If you are using a Promise to handle callbacks, be aware that implementations following the A+ standard adhere to run-to-completion semantics and so Promise resolution occurs on a subsequent tick and therefore after the transaction commits.',
code: 11
};
return;
}
if (this.readOnly && READ_ONLY_REGEX.test(sql)) {
this.handleStatementFailure(error, {
message: 'invalid sql for a read-only transaction'
});
return;
}
this.addStatement(sql, values, success, error);
};
SQLitePluginTransaction.prototype.addStatement = function(sql, values, success, error) {
var j, len1, params, t, v;
params = [];
if (!!values && values.constructor === Array) {
for (j = 0, len1 = values.length; j < len1; j++) {
v = values[j];
t = typeof v;
params.push((v === null || v === void 0 || t === 'number' || t === 'string' ? v : v instanceof Blob ? v.valueOf() : v.toString()));
}
}
this.executes.push({
success: success,
error: error,
sql: sql,
params: params
});
};
SQLitePluginTransaction.prototype.handleStatementSuccess = function(handler, response) {
var payload, rows;
if (!handler) {
return;
}
rows = response.rows || [];
payload = {
rows: {
item: function(i) {
return rows[i];
},
length: rows.length
},
rowsAffected: response.rowsAffected || 0,
insertId: response.insertId || void 0
};
handler(this, payload);
};
SQLitePluginTransaction.prototype.handleStatementFailure = function(handler, response) {
if (!handler) {
throw newSQLError("a statement with no error handler failed: " + response.message, response.code);
}
if (handler(this, response) !== false) {
throw newSQLError("a statement error callback did not return false: " + response.message, response.code);
}
};
SQLitePluginTransaction.prototype.run = function() {
var batchExecutes, handlerFor, i, mycb, mycbmap, request, tropts, tx, txFailure, waiting;
txFailure = null;
tropts = [];
batchExecutes = this.executes;
waiting = batchExecutes.length;
this.executes = [];
tx = this;
handlerFor = function(index, didSucceed) {
return function(response) {
var err, error1;
try {
if (didSucceed) {
tx.handleStatementSuccess(batchExecutes[index].success, response);
} else {
tx.handleStatementFailure(batchExecutes[index].error, newSQLError(response));
}
} catch (error1) {
err = error1;
if (!txFailure) {
txFailure = newSQLError(err);
}
}
if (--waiting === 0) {
if (txFailure) {
tx.abort(txFailure);
} else if (tx.executes.length > 0) {
tx.run();
} else {
tx.finish();
}
}
};
};
i = 0;
mycbmap = {};
while (i < batchExecutes.length) {
request = batchExecutes[i];
mycbmap[i] = {
success: handlerFor(i, true),
error: handlerFor(i, false)
};
tropts.push({
qid: 1111,
sql: request.sql,
params: request.params
});
i++;
}
mycb = function(result) {
var j, last, q, r, ref, res, type;
last = result.length - 1;
for (i = j = 0, ref = last; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) {
r = result[i];
type = r.type;
res = r.result;
q = mycbmap[i];
if (q) {
if (q[type]) {
q[type](res);
}
}
}
};
cordova.exec(mycb, null, "SQLitePlugin", "backgroundExecuteSqlBatch", [
{
dbargs: {
dbname: this.db.dbname
},
executes: tropts
}
]);
};
SQLitePluginTransaction.prototype.abort = function(txFailure) {
var failed, succeeded, tx;
if (this.finalized) {
return;
}
tx = this;
succeeded = function(tx) {
txLocks[tx.db.dbname].inProgress = false;
tx.db.startNextTransaction();
if (tx.error) {
tx.error(txFailure);
}
};
failed = function(tx, err) {
txLocks[tx.db.dbname].inProgress = false;
tx.db.startNextTransaction();
if (tx.error) {
tx.error(newSQLError("error while trying to roll back: " + err.message, err.code));
}
};
this.finalized = true;
if (this.txlock) {
this.addStatement("ROLLBACK", [], succeeded, failed);
this.run();
} else {
succeeded(tx);
}
};
SQLitePluginTransaction.prototype.finish = function() {
var failed, succeeded, tx;
if (this.finalized) {
return;
}
tx = this;
succeeded = function(tx) {
txLocks[tx.db.dbname].inProgress = false;
tx.db.startNextTransaction();
if (tx.success) {
tx.success();
}
};
failed = function(tx, err) {
txLocks[tx.db.dbname].inProgress = false;
tx.db.startNextTransaction();
if (tx.error) {
tx.error(newSQLError("error while trying to commit: " + err.message, err.code));
}
};
this.finalized = true;
if (this.txlock) {
this.addStatement("COMMIT", [], succeeded, failed);
this.run();
} else {
succeeded(tx);
}
};
SQLitePluginTransaction.prototype.abortFromQ = function(sqlerror) {
if (this.error) {
this.error(sqlerror);
}
};
dblocations = ["docs", "libs", "nosync"];
SQLiteFactory = {
/*
NOTE: this function should NOT be translated from Javascript
back to CoffeeScript by js2coffee.
If this function is edited in Javascript then someone will
have to translate it back to CoffeeScript by hand.
*/
opendb: argsArray(function(args) {
var dblocation, errorcb, first, okcb, openargs;
if (args.length < 1) {
return null;
}
first = args[0];
openargs = null;
okcb = null;
errorcb = null;
if (first.constructor === String) {
openargs = {
name: first
};
if (args.length >= 5) {
okcb = args[4];
if (args.length > 5) {
errorcb = args[5];
}
}
} else {
openargs = first;
if (args.length >= 2) {
okcb = args[1];
if (args.length > 2) {
errorcb = args[2];
}
}
}
dblocation = !!openargs.location ? dblocations[openargs.location] : null;
openargs.dblocation = dblocation || dblocations[0];
if (!!openargs.createFromLocation && openargs.createFromLocation === 1) {
openargs.createFromResource = "1";
}
if (!!openargs.androidDatabaseImplementation && openargs.androidDatabaseImplementation === 2) {
openargs.androidOldDatabaseImplementation = 1;
}
if (!!openargs.androidLockWorkaround && openargs.androidLockWorkaround === 1) {
openargs.androidBugWorkaround = 1;
}
return new SQLitePlugin(openargs, okcb, errorcb);
}),
deleteDb: function(first, success, error) {
var args, dblocation;
args = {};
if (first.constructor === String) {
args.path = first;
args.dblocation = dblocations[0];
} else {
if (!(first && first['name'])) {
throw new Error("Please specify db name");
}
args.path = first.name;
dblocation = !!first.location ? dblocations[first.location] : null;
args.dblocation = dblocation || dblocations[0];
}
delete SQLitePlugin.prototype.openDBs[args.path];
return cordova.exec(success, error, "SQLitePlugin", "delete", [args]);
}
};
root.sqlitePlugin = {
sqliteFeatures: {
isSQLitePlugin: true
},
openDatabase: SQLiteFactory.opendb,
deleteDatabase: SQLiteFactory.deleteDb
};
}).call(this);
});

View File

@ -0,0 +1,11 @@
cordova.define("me.rahul.plugins.sqlDB.sqlDB", function(require, exports, module) { var exec = require('cordova/exec');
exports.copy = function(dbname, location, success, error) {
exec(success, error, "sqlDB", "copy", [dbname, location]);
};
exports.remove = function(dbname, location, success,error) {
exec(success, error, "sqlDB", "remove", [dbname, location]);
};
});

Binary file not shown.

View File

@ -1,11 +1,11 @@
<ion-view view-title="Gekaufte Rosen"> <ion-view view-title="Gekaufte Rosen">
<ion-content> <ion-content class="padding">
<div style="padding-bottom:15px;"> <div style="padding-bottom:15px;">
<div class="item item-divider oswald">Hier können Sie Ihre gekauften Rosen ansehen oder nochmals versenden!</div>
<ul class="list card">
<div class="item item-divider oleo">Hier können Sie Ihre gekauften Rosen ansehen oder nochmals versenden!</div>
<ul class="list">
<a class="item item-thumbnail-left item-button-right" href="#" ng-repeat="i in items" ng-model="items"> <a class="item item-thumbnail-left item-button-right" href="#" ng-repeat="i in items" ng-model="items">
<img src="img/rose1.png"> <img src="img/rose1.png">
<h1>{{i.name}}</h1> <h1>{{i.name}}</h1>
@ -21,10 +21,6 @@
</a> </a>
</ul> </ul>
</div> </div>

View File

@ -1,43 +1,28 @@
<ion-view view-title='Rosen kaufen'> <ion-view view-title='Rosen kaufen'>
<ion-content class="padding"> <ion-content class="padding">
<div class="list card"> <div class="list card">
<div class="item item-divider oswald">Wolle Rose kaufen?</div> <div class="item item-divider oleo">Schenken Sie einem besonderen Menschen eine außergewöhnliche Aufmerksamkeit!</div>
<div class="item item-body lobster">
<div style="padding-bottom:25px;">
Schenken Sie einem besonderen Menschen eine aussergewöhnliche Aufmerksamkeit.
</div>
<div class="item item-divider oswald">An wen wollen Sie die Rose verschenken</div>
<div class="list list-inset" style="padding-bottom:15px;">
<label class="item item-input item-stacked-label"> <label class="item item-input item-stacked-label">
<span class="input-label">Name Ihres Liebsten:</span> <span class="input-label">Name Ihres Liebsten:</span>
<input type="text" placeholder="" class="lobster" ng-model="$parent.name"> <input type="text" placeholder="" class="oleo" ng-model="$parent.name">
</label> </label>
<label class="item item-input item-stacked-label"> <label class="item item-input item-stacked-label">
<span class="input-label">Ihre persönliche Nachricht:</span> <span class="input-label">Ihre persönliche Nachricht:</span>
<textarea placeholder="" rows="4" cols="10" class="lobster" ng-model="$parent.freitext"></textarea> <textarea placeholder="" rows="4" cols="10" class="oleo" ng-model="$parent.freitext"></textarea>
</label> </label>
</div> </div>
<div style="padding-bottom:15px;"> <div style="padding-bottom:15px;">
<div class="item item-divider oleo">Wählen Sie Ihre Rosen aus</div> <div class="item item-divider oleo">Wählen Sie Ihre Rosen aus:</div>
<ion-list> <ion-list>
<ion-radio ng-model="$parent.preis" ng-value="'1xrose'" class="item item-thumbnail-left item-text-wrap"><img src="img/rose1.png"><h2 class="lobster">1 Rose </h2><p>Preis: 0,50 € (inkl. Mwst 0,59 €)</p></ion-radio> <ion-radio ng-model="$parent.preis" ng-value="'1xrose'" class="item item-thumbnail-left item-text-wrap"><img src="img/rose1.png"><h2 class="oleo">1 Rose </h2><p>Preis: 0,50 € (zzgl. gesetzl. MwSt.)</p></ion-radio>
<ion-radio ng-model="$parent.preis" ng-value="'3xrose'" class="item item-thumbnail-left item-text-wrap"><img src="img/rose3.png"><h2 class="lobster">3 Rosen</h2><p>Preis: 1,00 € (inkl. Mwst 1,19 €)</p></ion-radio> <ion-radio ng-model="$parent.preis" ng-value="'3xrose'" class="item item-thumbnail-left item-text-wrap"><img src="img/rose3.png"><h2 class="oleo">3 Rosen</h2><p>Preis: 1,00 € (zzgl. gesetzl. MwSt.)</p></ion-radio>
<ion-radio ng-model="$parent.preis" ng-value="'9xrose'" class="item item-thumbnail-left item-text-wrap"><img src="img/rose10.png"><h2 class="lobster">9 Rosen</h2><p>Preis: 2,00 € (inkl. Mwst 2,38 €)</p></ion-radio> <ion-radio ng-model="$parent.preis" ng-value="'9xrose'" class="item item-thumbnail-left item-text-wrap"><img src="img/rose10.png"><h2 class="oleo">9 Rosen</h2><p>Preis: 2,00 € (zzgl. gesetzl. MwSt.)</p></ion-radio>
</ion-list> </ion-list>
<label class="item item-input item-select"> </div>
<div class="input-label">
Farbe <div class="list card">
</div> <div class="item item-divider oleo">Rosen jetzt kaufen?</div>
<select>
<option selected>Rot</option>
<option>Gelb</option>
<option>Weiß</option>
</select>
</label>
</div>
<div class="item item-divider oswald">Rosen kaufen?</div>
<div class="row"> <div class="row">
<div class="col col-50"><button class="button button-full button-small icon-left ion-image button-custom" ng-disabled="!$parent.preis || !$parent.name || !$parent.freitext" ng-click="validate()">Vorschau</button></div> <div class="col col-50"><button class="button button-full button-small icon-left ion-image button-custom" ng-disabled="!$parent.preis || !$parent.name || !$parent.freitext" ng-click="validate()">Vorschau</button></div>
<div class="col col-50"><button class="button button-full button-small icon-left ion-social-euro button-custom" ng-disabled="!$parent.preis || !$parent.freitext || !$parent.name" ng-click="buyRose()">Jetzt Kaufen</button></div> <div class="col col-50"><button class="button button-full button-small icon-left ion-social-euro button-custom" ng-disabled="!$parent.preis || !$parent.freitext || !$parent.name" ng-click="buyRose()">Jetzt Kaufen</button></div>
@ -47,7 +32,7 @@
<div class="col col-50"><button class="button button-full button-small icon-left ion-image button-custom" ng-disabled="!$parent.preis || !$parent.name || !$parent.freitext" ng-click="loaddata()">Laden</button></div> <div class="col col-50"><button class="button button-full button-small icon-left ion-image button-custom" ng-disabled="!$parent.preis || !$parent.name || !$parent.freitext" ng-click="loaddata()">Laden</button></div>
<div class="col col-50"><button class="button button-full button-small icon-left ion-social-euro button-custom" ng-disabled="!$parent.preis || !$parent.freitext || !$parent.name" ng-click="savedata()">Speichern</button></div> <div class="col col-50"><button class="button button-full button-small icon-left ion-social-euro button-custom" ng-disabled="!$parent.preis || !$parent.freitext || !$parent.name" ng-click="savedata()">Speichern</button></div>
</div> </div>
</div>
</div> </div>
</div> </div>

View File

@ -238,6 +238,22 @@ module.exports = [
"clobbers": [ "clobbers": [
"window.FileTransfer" "window.FileTransfer"
] ]
},
{
"file": "plugins/cordova-sqlite-storage/www/SQLitePlugin.js",
"id": "cordova-sqlite-storage.SQLitePlugin",
"pluginId": "cordova-sqlite-storage",
"clobbers": [
"SQLitePlugin"
]
},
{
"file": "plugins/me.rahul.plugins.sqlDB/www/sqlDB.js",
"id": "me.rahul.plugins.sqlDB.sqlDB",
"pluginId": "me.rahul.plugins.sqlDB",
"clobbers": [
"window.plugins.sqlDB"
]
} }
]; ];
module.exports.metadata = module.exports.metadata =
@ -252,7 +268,9 @@ module.exports.metadata =
"ionic-plugin-keyboard": "1.0.8", "ionic-plugin-keyboard": "1.0.8",
"cordova-plugin-inappbrowser": "1.1.1", "cordova-plugin-inappbrowser": "1.1.1",
"cordova-plugin-file": "3.0.0", "cordova-plugin-file": "3.0.0",
"cordova-plugin-file-transfer": "1.4.0" "cordova-plugin-file-transfer": "1.4.0",
"cordova-sqlite-storage": "0.7.15-pre",
"me.rahul.plugins.sqlDB": "1.0.3"
} }
// BOTTOM OF METADATA // BOTTOM OF METADATA
}); });

View File

@ -3,6 +3,7 @@
font-family: 'Lobster', cursive; font-family: 'Lobster', cursive;
} }
.lobsterMiddle{ .lobsterMiddle{
font-family: 'Lobster', cursive; font-family: 'Lobster', cursive;
font-size: 1.2em; font-size: 1.2em;
@ -22,35 +23,45 @@
.item-divider{ .item-divider{
background-image: linear-gradient( background-image: linear-gradient(
hsla(340, 100%, 35%, 1) 20%, hsla(0, 94%, 33%, 1) 20%,
hsl(340,100%,54%) 90% hsl(0,94%,54%) 90%
); );
color:white; color:white;
} }
.tabs-addddssertive > .tabs{ .tabs-addddssertive > .tabs{
background-image: linear-gradient( background-image: linear-gradient(
hsla(340, 100%, 35%, 1) 20%, hsla(0, 94%, 33%, 1) 20%,
hsl(340,100%,54%) 90% hsl(0,94%,54%) 90%
) !important; ) !important;
} }
.tabs-assertive > .tabs{ .tabs-assertive > .tabs{
background-color: #BD0340 !important; background-color: #A50505 !important;
} }
.bar-custom{ .bar-custom{
background-image: linear-gradient( background-image: linear-gradient(
hsla(340, 100%, 35%, 1) 20%, hsla(0, 94%, 33%, 1) 20%,
hsl(340,100%,54%) 90% hsl(0,94%,54%) 90%
) !important; ) !important;
font-family: 'Bubblegum Sans', cursive;
} }
.button-custom{ .button-custom{
background-color: #C60545; font-family: 'Bubblegum Sans', cursive;
text-decoration: none; background-color: #A50505;
border: 1px solid #C60545; text-decoration: none;
border-color: #F9135F #F9135F #F9135F #F9135F; border: 1px solid #A50505;
color: #fff color: #fff
} }
.oleo{
font-family: 'Bubblegum Sans', cursive;
font-size: 1.3em !important;
}

View File

@ -8,6 +8,7 @@
<link href="lib/ionic/css/ionic.css" rel="stylesheet"> <link href="lib/ionic/css/ionic.css" rel="stylesheet">
<link href='https://fonts.googleapis.com/css?family=Lobster' rel='stylesheet' type='text/css'> <link href='https://fonts.googleapis.com/css?family=Lobster' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Oswald:400,300,700' rel='stylesheet' type='text/css'> <link href='https://fonts.googleapis.com/css?family=Oswald:400,300,700' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Bubblegum+Sans' rel='stylesheet' type='text/css'>
<link href="css/style.css" rel="stylesheet"> <link href="css/style.css" rel="stylesheet">
<!-- IF using Sass (run gulp sass first), then uncomment below and remove the CSS includes above <!-- IF using Sass (run gulp sass first), then uncomment below and remove the CSS includes above
@ -16,7 +17,7 @@
<!-- ionic/angularjs js --> <!-- ionic/angularjs js -->
<script src="lib/ionic/js/ionic.bundle.js"></script> <script src="lib/ionic/js/ionic.bundle.js"></script>
<script src="js/ng-cordova.min.js"></script>
<!-- cordova script (this will be a 404 during development) --> <!-- cordova script (this will be a 404 during development) -->
<script src="cordova.js"></script> <script src="cordova.js"></script>

View File

@ -5,21 +5,55 @@
// the 2nd parameter is an array of 'requires' // the 2nd parameter is an array of 'requires'
// 'starter.services' is found in services.js // 'starter.services' is found in services.js
// 'starter.controllers' is found in controllers.js // 'starter.controllers' is found in controllers.js
angular.module('starter', ['ionic', 'starter.controllers', 'starter.services']) var db;
.run(function($ionicPlatform, $ionicPopup) { angular.module('starter', ['ionic', 'ngCordova', 'starter.controllers', 'starter.services'])
.run(function($ionicPlatform, $window, $ionicHistory, $database, $ionicPopup, $state, $localstorage, $rootScope) {
$ionicPlatform.ready(function() { $ionicPlatform.ready(function() {
// Hide the accessory bar by default (remove this to show the accessory bar above the keyboard // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
// for form inputs) // for form inputs)
$ionicPlatform.registerBackButtonAction(function(event) {
// Handle Android back button to avoid the application exits accidentaly
if ($state.current.name=="tab.dash") {
$ionicPopup.confirm({
title: 'System-Hinweis',
template: 'Möchten Sie die App beenden?'
}).then(function(res) {
if (res) {
ionic.Platform.exitApp();
}
});
} else {
$ionicHistory.clearCache();
$ionicHistory.nextViewOptions({
historyRoot: true
});
$state.go('tab.dash');
}
}, 100);
if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) { if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true); cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
cordova.plugins.Keyboard.disableScroll(true); cordova.plugins.Keyboard.disableScroll(true);
} }
if (window.StatusBar) {
// org.apache.cordova.statusbar required // Mandatory for InAppBrowser plugin
StatusBar.styleDefault(); if(window.cordova){
window.open = cordova.InAppBrowser.open;
} }
// Copy the populated database to mobile device destination
//if (window.sqlitePlugin && window.cordova) {
if (window.sqlitePlugin && window.cordova) {
window.plugins.sqlDB.copy("raataar_wrk.db", function (e) {
console.log(e);
});
}
function successHandler (result) { function successHandler (result) {
var strResult = ""; var strResult = "";
@ -39,7 +73,16 @@ angular.module('starter', ['ionic', 'starter.controllers', 'starter.services'])
if((window.device && device.platform == "Android") && typeof inappbilling !== "undefined") { if((window.device && device.platform == "Android") && typeof inappbilling !== "undefined") {
inappbilling.init(successHandler, errorHandler, {showLog:true}); inappbilling.init(successHandler, errorHandler, {showLog:true});
} }
// Initialize database through $database service
db = $database.initDB();
if (window.StatusBar) {
// org.apache.cordova.statusbar required
StatusBar.styleDefault();
}
}); });
}) })

View File

@ -1,6 +1,6 @@
angular.module('starter.controllers', []) angular.module('starter.controllers', [])
.controller('DashCtrl', function($scope,$ionicPopup,$http, $localstorage) { .controller('DashCtrl', function($scope,$ionicPopup,$http, $database) {
$scope.preis=""; $scope.preis="";
$scope.name=""; $scope.name="";
@ -8,7 +8,9 @@ angular.module('starter.controllers', [])
$scope.items=[]; $scope.items=[];
$scope.item=[]; $scope.item=[];
$scope.items=$localstorage.getObject('items'); //db = Database.getDb();
//$scope.items=$localstorage.getObject('items');
$scope.validate = function() { $scope.validate = function() {
@ -22,16 +24,30 @@ angular.module('starter.controllers', [])
}; };
$scope.loaddata = function() { $scope.loaddata = function() {
$scope.items=$localstorage.getObject('items'); $scope.items=[];
alert(JSON.stringify($scope.items)); $database.getAllBuys().then(function (result) {
if(result.length>0){
alert("daten da");
for(i=0;i<result.length;i++){
$scope.items.push(result[i]);
alert(i);
}
} else {
$scope.items=[];
}
});
}; };
$scope.savedata = function() { $scope.savedata = function() {
$scope.item={name: $scope.name, freitext: $scope.freitext, rosen: $scope.preis, bild: "test"}; $scope.item={name: $scope.name, bild: "test"};
alert(JSON.stringify($scope.item));
$scope.items.push($scope.item); $scope.items.push($scope.item);
$localstorage.setObject('items',$scope.items); $database.setBuys($scope.name,"test");
}; };
function successBuyHandler (result) { function successBuyHandler (result) {
@ -78,14 +94,22 @@ angular.module('starter.controllers', [])
}) })
.controller('BuysCtrl', function($scope, $localstorage,$location) { .controller('BuysCtrl', function($scope, $database,$location) {
$scope.items=[]; $scope.items=[];
$scope.items=$localstorage.getObject('items'); //$scope.items=$localstorage.getObject('items');
$scope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) { $scope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
if ($location.path() == "/tab/buys") { if ($location.path() == "/tab/buys") {
$scope.items=$localstorage.getObject('items'); $database.getAllBuys().then(function (result) {
} if(result.length>0){
for(i=0;i<result.length;i++){
$scope.items.push(result[i]);
}
} else {
$scope.items=[];
}
});
}
}); });

File diff suppressed because one or more lines are too long

View File

@ -1,19 +1,94 @@
angular.module('starter.services', []) angular.module('starter.services', [])
.factory('$localstorage', ['$window', function($window) { .factory('$localstorage', ['$window', function($window) {
return { return {
set: function(key, value) { set: function(key, value) {
$window.localStorage[key] = value; $window.localStorage[key] = value;
}, },
get: function(key, defaultValue) { get: function(key, defaultValue) {
return $window.localStorage[key] || defaultValue; return $window.localStorage[key] || defaultValue;
}, },
setObject: function(key, value) { setObject: function(key, value) {
$window.localStorage[key] = JSON.stringify(value); $window.localStorage[key] = angular.toJson(value);
}, },
getObject: function(key) { getObject: function(key) {
return JSON.parse($window.localStorage[key] || '{}'); return JSON.parse($window.localStorage[key] || '[]');
},
destroy: function(key) {
$window.localStorage.removeItem(key);
},
log: function(key, defaultValue) {
console.log($window.localStorage[key] || defaultValue);
},
logObject: function(key) {
console.log(JSON.stringify($window.localStorage[key] || '{}'));
}
};
}])
.factory('$database',['$cordovaSQLite', function($cordovaSQLite) {
var self = this;
self.db = null;
var query;
//this updateTable function is for ilustrative use here, it can be changed to manage all queries indeed.
updateTable=function (table, column, value, condition) {
if(!condition){
query = "UPDATE " + table + " SET " + column + " = " + value;
} else{
query = "UPDATE " + table + " SET " + column + " = " + value + " WHERE " + condition;
} }
} return $cordovaSQLite.execute(self.db,query,[]).
}]); then(function(result) {
console.log(query);
query=null;
return result;
},function(error) {
console.error(error);
query=null;
return error;
});
};
return {
initDB: function() {
if(window.cordova){
//self.db = sqlitePlugin.openDatabase({name: "data.db", location: 2, createFromLocation: 1});
self.db = $cordovaSQLite.openDB("raataar_wrk.db");
} else {
self.db = window.openDatabase('raataar_wrk.db','1','my',800*1024); // only available when WebSQL is available in Browser
}
console.log('Database opened');
return self.db;
},
setBuys: function(itemName,itemBild) {
query="INSERT INTO buys (Name, Bild) VALUES (?,?);";
return $cordovaSQLite.execute(self.db,query,[itemName,itemBild]).
then(function(result) {
console.log("Gespeichert");
query=null;
}, function(error) {
console.error(error);
});
},
getAllBuys: function(){
var ArrayQ=[];
query="SELECT * FROM buys order by id ASC";
return $cordovaSQLite.execute(self.db,query).
then(function(result) {
for(j=0;j<result.rows.length;j++){
var List={};
//console.log(result.rows.item(j));
List.name=result.rows.item(j).Name;
List.bild=result.rows.item(j).Bild;
ArrayQ.push(List);
}
//console.log(ArrayQ);
return ArrayQ;
},function(e){
console.error(error);
return error;
});
}
};
}]);

View File

@ -1,4 +1,4 @@
@charset "UTF-8"; @charset "UTF-8";
/*! /*!
* Copyright 2014 Drifty Co. * Copyright 2014 Drifty Co.
* http://drifty.com/ * http://drifty.com/

View File

@ -0,0 +1,576 @@
cordova.define("cordova-sqlite-storage.SQLitePlugin", function(require, exports, module) { (function() {
var DB_STATE_INIT, DB_STATE_OPEN, READ_ONLY_REGEX, SQLiteFactory, SQLitePlugin, SQLitePluginTransaction, argsArray, dblocations, newSQLError, nextTick, root, txLocks;
root = this;
READ_ONLY_REGEX = /^\s*(?:drop|delete|insert|update|create)\s/i;
DB_STATE_INIT = "INIT";
DB_STATE_OPEN = "OPEN";
txLocks = {};
newSQLError = function(error, code) {
var sqlError;
sqlError = error;
if (!code) {
code = 0;
}
if (!sqlError) {
sqlError = new Error("a plugin had an error but provided no response");
sqlError.code = code;
}
if (typeof sqlError === "string") {
sqlError = new Error(error);
sqlError.code = code;
}
if (!sqlError.code && sqlError.message) {
sqlError.code = code;
}
if (!sqlError.code && !sqlError.message) {
sqlError = new Error("an unknown error was returned: " + JSON.stringify(sqlError));
sqlError.code = code;
}
return sqlError;
};
nextTick = window.setImmediate || function(fun) {
window.setTimeout(fun, 0);
};
/*
Utility that avoids leaking the arguments object. See
https://www.npmjs.org/package/argsarray
*/
argsArray = function(fun) {
return function() {
var args, i, len;
len = arguments.length;
if (len) {
args = [];
i = -1;
while (++i < len) {
args[i] = arguments[i];
}
return fun.call(this, args);
} else {
return fun.call(this, []);
}
};
};
SQLitePlugin = function(openargs, openSuccess, openError) {
var dbname;
if (!(openargs && openargs['name'])) {
throw newSQLError("Cannot create a SQLitePlugin db instance without a db name");
}
dbname = openargs.name;
if (typeof dbname !== 'string') {
throw newSQLError('sqlite plugin database name must be a string');
}
this.openargs = openargs;
this.dbname = dbname;
this.openSuccess = openSuccess;
this.openError = openError;
this.openSuccess || (this.openSuccess = function() {
console.log("DB opened: " + dbname);
});
this.openError || (this.openError = function(e) {
console.log(e.message);
});
this.open(this.openSuccess, this.openError);
};
SQLitePlugin.prototype.databaseFeatures = {
isSQLitePluginDatabase: true
};
SQLitePlugin.prototype.openDBs = {};
SQLitePlugin.prototype.addTransaction = function(t) {
if (!txLocks[this.dbname]) {
txLocks[this.dbname] = {
queue: [],
inProgress: false
};
}
txLocks[this.dbname].queue.push(t);
if (this.dbname in this.openDBs && this.openDBs[this.dbname] !== DB_STATE_INIT) {
this.startNextTransaction();
} else {
if (this.dbname in this.openDBs) {
console.log('new transaction is waiting for open operation');
} else {
console.log('database is closed, new transaction is [stuck] waiting until db is opened again!');
}
}
};
SQLitePlugin.prototype.transaction = function(fn, error, success) {
if (!this.openDBs[this.dbname]) {
error(newSQLError('database not open'));
return;
}
this.addTransaction(new SQLitePluginTransaction(this, fn, error, success, true, false));
};
SQLitePlugin.prototype.readTransaction = function(fn, error, success) {
if (!this.openDBs[this.dbname]) {
error(newSQLError('database not open'));
return;
}
this.addTransaction(new SQLitePluginTransaction(this, fn, error, success, false, true));
};
SQLitePlugin.prototype.startNextTransaction = function() {
var self;
self = this;
nextTick((function(_this) {
return function() {
var txLock;
if (!(_this.dbname in _this.openDBs) || _this.openDBs[_this.dbname] !== DB_STATE_OPEN) {
console.log('cannot start next transaction: database not open');
return;
}
txLock = txLocks[self.dbname];
if (!txLock) {
console.log('cannot start next transaction: database connection is lost');
return;
} else if (txLock.queue.length > 0 && !txLock.inProgress) {
txLock.inProgress = true;
txLock.queue.shift().start();
}
};
})(this));
};
SQLitePlugin.prototype.abortAllPendingTransactions = function() {
var j, len1, ref, tx, txLock;
txLock = txLocks[this.dbname];
if (!!txLock && txLock.queue.length > 0) {
ref = txLock.queue;
for (j = 0, len1 = ref.length; j < len1; j++) {
tx = ref[j];
tx.abortFromQ(newSQLError('Invalid database handle'));
}
txLock.queue = [];
txLock.inProgress = false;
}
};
SQLitePlugin.prototype.open = function(success, error) {
var openerrorcb, opensuccesscb;
if (this.dbname in this.openDBs) {
console.log('database already open: ' + this.dbname);
nextTick((function(_this) {
return function() {
success(_this);
};
})(this));
} else {
console.log('OPEN database: ' + this.dbname);
opensuccesscb = (function(_this) {
return function() {
var txLock;
if (!_this.openDBs[_this.dbname]) {
console.log('database was closed during open operation');
}
if (_this.dbname in _this.openDBs) {
_this.openDBs[_this.dbname] = DB_STATE_OPEN;
}
if (!!success) {
success(_this);
}
txLock = txLocks[_this.dbname];
if (!!txLock && txLock.queue.length > 0 && !txLock.inProgress) {
_this.startNextTransaction();
}
};
})(this);
openerrorcb = (function(_this) {
return function() {
console.log('OPEN database: ' + _this.dbname + ' failed, aborting any pending transactions');
if (!!error) {
error(newSQLError('Could not open database'));
}
delete _this.openDBs[_this.dbname];
_this.abortAllPendingTransactions();
};
})(this);
this.openDBs[this.dbname] = DB_STATE_INIT;
cordova.exec(opensuccesscb, openerrorcb, "SQLitePlugin", "open", [this.openargs]);
}
};
SQLitePlugin.prototype.close = function(success, error) {
if (this.dbname in this.openDBs) {
if (txLocks[this.dbname] && txLocks[this.dbname].inProgress) {
console.log('cannot close: transaction is in progress');
error(newSQLError('database cannot be closed while a transaction is in progress'));
return;
}
console.log('CLOSE database: ' + this.dbname);
delete this.openDBs[this.dbname];
if (txLocks[this.dbname]) {
console.log('closing db with transaction queue length: ' + txLocks[this.dbname].queue.length);
} else {
console.log('closing db with no transaction lock state');
}
cordova.exec(success, error, "SQLitePlugin", "close", [
{
path: this.dbname
}
]);
} else {
console.log('cannot close: database is not open');
if (error) {
nextTick(function() {
return error();
});
}
}
};
SQLitePlugin.prototype.executeSql = function(statement, params, success, error) {
var myerror, myfn, mysuccess;
mysuccess = function(t, r) {
if (!!success) {
return success(r);
}
};
myerror = function(t, e) {
if (!!error) {
return error(e);
}
};
myfn = function(tx) {
tx.addStatement(statement, params, mysuccess, myerror);
};
this.addTransaction(new SQLitePluginTransaction(this, myfn, null, null, false, false));
};
SQLitePluginTransaction = function(db, fn, error, success, txlock, readOnly) {
if (typeof fn !== "function") {
/*
This is consistent with the implementation in Chrome -- it
throws if you pass anything other than a function. This also
prevents us from stalling our txQueue if somebody passes a
false value for fn.
*/
throw newSQLError("transaction expected a function");
}
this.db = db;
this.fn = fn;
this.error = error;
this.success = success;
this.txlock = txlock;
this.readOnly = readOnly;
this.executes = [];
if (txlock) {
this.addStatement("BEGIN", [], null, function(tx, err) {
throw newSQLError("unable to begin transaction: " + err.message, err.code);
});
}
};
SQLitePluginTransaction.prototype.start = function() {
var err, error1;
try {
this.fn(this);
this.run();
} catch (error1) {
err = error1;
txLocks[this.db.dbname].inProgress = false;
this.db.startNextTransaction();
if (this.error) {
this.error(newSQLError(err));
}
}
};
SQLitePluginTransaction.prototype.executeSql = function(sql, values, success, error) {
if (this.finalized) {
throw {
message: 'InvalidStateError: DOM Exception 11: This transaction is already finalized. Transactions are committed after its success or failure handlers are called. If you are using a Promise to handle callbacks, be aware that implementations following the A+ standard adhere to run-to-completion semantics and so Promise resolution occurs on a subsequent tick and therefore after the transaction commits.',
code: 11
};
return;
}
if (this.readOnly && READ_ONLY_REGEX.test(sql)) {
this.handleStatementFailure(error, {
message: 'invalid sql for a read-only transaction'
});
return;
}
this.addStatement(sql, values, success, error);
};
SQLitePluginTransaction.prototype.addStatement = function(sql, values, success, error) {
var j, len1, params, t, v;
params = [];
if (!!values && values.constructor === Array) {
for (j = 0, len1 = values.length; j < len1; j++) {
v = values[j];
t = typeof v;
params.push((v === null || v === void 0 || t === 'number' || t === 'string' ? v : v instanceof Blob ? v.valueOf() : v.toString()));
}
}
this.executes.push({
success: success,
error: error,
sql: sql,
params: params
});
};
SQLitePluginTransaction.prototype.handleStatementSuccess = function(handler, response) {
var payload, rows;
if (!handler) {
return;
}
rows = response.rows || [];
payload = {
rows: {
item: function(i) {
return rows[i];
},
length: rows.length
},
rowsAffected: response.rowsAffected || 0,
insertId: response.insertId || void 0
};
handler(this, payload);
};
SQLitePluginTransaction.prototype.handleStatementFailure = function(handler, response) {
if (!handler) {
throw newSQLError("a statement with no error handler failed: " + response.message, response.code);
}
if (handler(this, response) !== false) {
throw newSQLError("a statement error callback did not return false: " + response.message, response.code);
}
};
SQLitePluginTransaction.prototype.run = function() {
var batchExecutes, handlerFor, i, mycb, mycbmap, request, tropts, tx, txFailure, waiting;
txFailure = null;
tropts = [];
batchExecutes = this.executes;
waiting = batchExecutes.length;
this.executes = [];
tx = this;
handlerFor = function(index, didSucceed) {
return function(response) {
var err, error1;
try {
if (didSucceed) {
tx.handleStatementSuccess(batchExecutes[index].success, response);
} else {
tx.handleStatementFailure(batchExecutes[index].error, newSQLError(response));
}
} catch (error1) {
err = error1;
if (!txFailure) {
txFailure = newSQLError(err);
}
}
if (--waiting === 0) {
if (txFailure) {
tx.abort(txFailure);
} else if (tx.executes.length > 0) {
tx.run();
} else {
tx.finish();
}
}
};
};
i = 0;
mycbmap = {};
while (i < batchExecutes.length) {
request = batchExecutes[i];
mycbmap[i] = {
success: handlerFor(i, true),
error: handlerFor(i, false)
};
tropts.push({
qid: 1111,
sql: request.sql,
params: request.params
});
i++;
}
mycb = function(result) {
var j, last, q, r, ref, res, type;
last = result.length - 1;
for (i = j = 0, ref = last; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) {
r = result[i];
type = r.type;
res = r.result;
q = mycbmap[i];
if (q) {
if (q[type]) {
q[type](res);
}
}
}
};
cordova.exec(mycb, null, "SQLitePlugin", "backgroundExecuteSqlBatch", [
{
dbargs: {
dbname: this.db.dbname
},
executes: tropts
}
]);
};
SQLitePluginTransaction.prototype.abort = function(txFailure) {
var failed, succeeded, tx;
if (this.finalized) {
return;
}
tx = this;
succeeded = function(tx) {
txLocks[tx.db.dbname].inProgress = false;
tx.db.startNextTransaction();
if (tx.error) {
tx.error(txFailure);
}
};
failed = function(tx, err) {
txLocks[tx.db.dbname].inProgress = false;
tx.db.startNextTransaction();
if (tx.error) {
tx.error(newSQLError("error while trying to roll back: " + err.message, err.code));
}
};
this.finalized = true;
if (this.txlock) {
this.addStatement("ROLLBACK", [], succeeded, failed);
this.run();
} else {
succeeded(tx);
}
};
SQLitePluginTransaction.prototype.finish = function() {
var failed, succeeded, tx;
if (this.finalized) {
return;
}
tx = this;
succeeded = function(tx) {
txLocks[tx.db.dbname].inProgress = false;
tx.db.startNextTransaction();
if (tx.success) {
tx.success();
}
};
failed = function(tx, err) {
txLocks[tx.db.dbname].inProgress = false;
tx.db.startNextTransaction();
if (tx.error) {
tx.error(newSQLError("error while trying to commit: " + err.message, err.code));
}
};
this.finalized = true;
if (this.txlock) {
this.addStatement("COMMIT", [], succeeded, failed);
this.run();
} else {
succeeded(tx);
}
};
SQLitePluginTransaction.prototype.abortFromQ = function(sqlerror) {
if (this.error) {
this.error(sqlerror);
}
};
dblocations = ["docs", "libs", "nosync"];
SQLiteFactory = {
/*
NOTE: this function should NOT be translated from Javascript
back to CoffeeScript by js2coffee.
If this function is edited in Javascript then someone will
have to translate it back to CoffeeScript by hand.
*/
opendb: argsArray(function(args) {
var dblocation, errorcb, first, okcb, openargs;
if (args.length < 1) {
return null;
}
first = args[0];
openargs = null;
okcb = null;
errorcb = null;
if (first.constructor === String) {
openargs = {
name: first
};
if (args.length >= 5) {
okcb = args[4];
if (args.length > 5) {
errorcb = args[5];
}
}
} else {
openargs = first;
if (args.length >= 2) {
okcb = args[1];
if (args.length > 2) {
errorcb = args[2];
}
}
}
dblocation = !!openargs.location ? dblocations[openargs.location] : null;
openargs.dblocation = dblocation || dblocations[0];
if (!!openargs.createFromLocation && openargs.createFromLocation === 1) {
openargs.createFromResource = "1";
}
if (!!openargs.androidDatabaseImplementation && openargs.androidDatabaseImplementation === 2) {
openargs.androidOldDatabaseImplementation = 1;
}
if (!!openargs.androidLockWorkaround && openargs.androidLockWorkaround === 1) {
openargs.androidBugWorkaround = 1;
}
return new SQLitePlugin(openargs, okcb, errorcb);
}),
deleteDb: function(first, success, error) {
var args, dblocation;
args = {};
if (first.constructor === String) {
args.path = first;
args.dblocation = dblocations[0];
} else {
if (!(first && first['name'])) {
throw new Error("Please specify db name");
}
args.path = first.name;
dblocation = !!first.location ? dblocations[first.location] : null;
args.dblocation = dblocation || dblocations[0];
}
delete SQLitePlugin.prototype.openDBs[args.path];
return cordova.exec(success, error, "SQLitePlugin", "delete", [args]);
}
};
root.sqlitePlugin = {
sqliteFeatures: {
isSQLitePlugin: true
},
openDatabase: SQLiteFactory.opendb,
deleteDatabase: SQLiteFactory.deleteDb
};
}).call(this);
});

View File

@ -0,0 +1,11 @@
cordova.define("me.rahul.plugins.sqlDB.sqlDB", function(require, exports, module) { var exec = require('cordova/exec');
exports.copy = function(dbname, location, success, error) {
exec(success, error, "sqlDB", "copy", [dbname, location]);
};
exports.remove = function(dbname, location, success,error) {
exec(success, error, "sqlDB", "remove", [dbname, location]);
};
});

View File

@ -1,11 +1,11 @@
<ion-view view-title="Gekaufte Rosen"> <ion-view view-title="Gekaufte Rosen">
<ion-content> <ion-content class="padding">
<div style="padding-bottom:15px;"> <div style="padding-bottom:15px;">
<div class="item item-divider oswald">Hier können Sie Ihre gekauften Rosen ansehen oder nochmals versenden!</div>
<ul class="list card">
<div class="item item-divider oleo">Hier können Sie Ihre gekauften Rosen ansehen oder nochmals versenden!</div>
<ul class="list">
<a class="item item-thumbnail-left item-button-right" href="#" ng-repeat="i in items" ng-model="items"> <a class="item item-thumbnail-left item-button-right" href="#" ng-repeat="i in items" ng-model="items">
<img src="img/rose1.png"> <img src="img/rose1.png">
<h1>{{i.name}}</h1> <h1>{{i.name}}</h1>
@ -21,10 +21,6 @@
</a> </a>
</ul> </ul>
</div> </div>

View File

@ -1,43 +1,28 @@
<ion-view view-title='Rosen kaufen'> <ion-view view-title='Rosen kaufen'>
<ion-content class="padding"> <ion-content class="padding">
<div class="list card"> <div class="list card">
<div class="item item-divider oswald">Wolle Rose kaufen?</div> <div class="item item-divider oleo">Schenken Sie einem besonderen Menschen eine außergewöhnliche Aufmerksamkeit!</div>
<div class="item item-body lobster">
<div style="padding-bottom:25px;">
Schenken Sie einem besonderen Menschen eine aussergewöhnliche Aufmerksamkeit.
</div>
<div class="item item-divider oswald">An wen wollen Sie die Rose verschenken</div>
<div class="list list-inset" style="padding-bottom:15px;">
<label class="item item-input item-stacked-label"> <label class="item item-input item-stacked-label">
<span class="input-label">Name Ihres Liebsten:</span> <span class="input-label">Name Ihres Liebsten:</span>
<input type="text" placeholder="" class="lobster" ng-model="$parent.name"> <input type="text" placeholder="" class="oleo" ng-model="$parent.name">
</label> </label>
<label class="item item-input item-stacked-label"> <label class="item item-input item-stacked-label">
<span class="input-label">Ihre persönliche Nachricht:</span> <span class="input-label">Ihre persönliche Nachricht:</span>
<textarea placeholder="" rows="4" cols="10" class="lobster" ng-model="$parent.freitext"></textarea> <textarea placeholder="" rows="4" cols="10" class="oleo" ng-model="$parent.freitext"></textarea>
</label> </label>
</div> </div>
<div style="padding-bottom:15px;"> <div style="padding-bottom:15px;">
<div class="item item-divider oswald">Wählen Sie Ihre Rosen aus</div> <div class="item item-divider oleo">Wählen Sie Ihre Rosen aus:</div>
<ion-list> <ion-list>
<ion-radio ng-model="$parent.preis" ng-value="'1xrose'" class="item item-thumbnail-left item-text-wrap"><img src="img/rose1.png"><h2 class="lobster">1 Rose </h2><p>Preis: 0,50 € (inkl. Mwst 0,59 €)</p></ion-radio> <ion-radio ng-model="$parent.preis" ng-value="'1xrose'" class="item item-thumbnail-left item-text-wrap"><img src="img/rose1.png"><h2 class="oleo">1 Rose </h2><p>Preis: 0,50 € (zzgl. gesetzl. MwSt.)</p></ion-radio>
<ion-radio ng-model="$parent.preis" ng-value="'3xrose'" class="item item-thumbnail-left item-text-wrap"><img src="img/rose3.png"><h2 class="lobster">3 Rosen</h2><p>Preis: 1,00 € (inkl. Mwst 1,19 €)</p></ion-radio> <ion-radio ng-model="$parent.preis" ng-value="'3xrose'" class="item item-thumbnail-left item-text-wrap"><img src="img/rose3.png"><h2 class="oleo">3 Rosen</h2><p>Preis: 1,00 € (zzgl. gesetzl. MwSt.)</p></ion-radio>
<ion-radio ng-model="$parent.preis" ng-value="'9xrose'" class="item item-thumbnail-left item-text-wrap"><img src="img/rose10.png"><h2 class="lobster">9 Rosen</h2><p>Preis: 2,00 € (inkl. Mwst 2,38 €)</p></ion-radio> <ion-radio ng-model="$parent.preis" ng-value="'9xrose'" class="item item-thumbnail-left item-text-wrap"><img src="img/rose10.png"><h2 class="oleo">9 Rosen</h2><p>Preis: 2,00 € (zzgl. gesetzl. MwSt.)</p></ion-radio>
</ion-list> </ion-list>
<label class="item item-input item-select"> </div>
<div class="input-label">
Farbe <div class="list card">
</div> <div class="item item-divider oleo">Rosen jetzt kaufen?</div>
<select>
<option selected>Rot</option>
<option>Gelb</option>
<option>Weiß</option>
</select>
</label>
</div>
<div class="item item-divider oswald">Rosen kaufen?</div>
<div class="row"> <div class="row">
<div class="col col-50"><button class="button button-full button-small icon-left ion-image button-custom" ng-disabled="!$parent.preis || !$parent.name || !$parent.freitext" ng-click="validate()">Vorschau</button></div> <div class="col col-50"><button class="button button-full button-small icon-left ion-image button-custom" ng-disabled="!$parent.preis || !$parent.name || !$parent.freitext" ng-click="validate()">Vorschau</button></div>
<div class="col col-50"><button class="button button-full button-small icon-left ion-social-euro button-custom" ng-disabled="!$parent.preis || !$parent.freitext || !$parent.name" ng-click="buyRose()">Jetzt Kaufen</button></div> <div class="col col-50"><button class="button button-full button-small icon-left ion-social-euro button-custom" ng-disabled="!$parent.preis || !$parent.freitext || !$parent.name" ng-click="buyRose()">Jetzt Kaufen</button></div>
@ -47,7 +32,7 @@
<div class="col col-50"><button class="button button-full button-small icon-left ion-image button-custom" ng-disabled="!$parent.preis || !$parent.name || !$parent.freitext" ng-click="loaddata()">Laden</button></div> <div class="col col-50"><button class="button button-full button-small icon-left ion-image button-custom" ng-disabled="!$parent.preis || !$parent.name || !$parent.freitext" ng-click="loaddata()">Laden</button></div>
<div class="col col-50"><button class="button button-full button-small icon-left ion-social-euro button-custom" ng-disabled="!$parent.preis || !$parent.freitext || !$parent.name" ng-click="savedata()">Speichern</button></div> <div class="col col-50"><button class="button button-full button-small icon-left ion-social-euro button-custom" ng-disabled="!$parent.preis || !$parent.freitext || !$parent.name" ng-click="savedata()">Speichern</button></div>
</div> </div>
</div>
</div> </div>
</div> </div>

View File

@ -15,6 +15,13 @@
sha1="565e3f24555b91a41efb1022e4b38d97307ce180"> sha1="565e3f24555b91a41efb1022e4b38d97307ce180">
<dex dex="C:\Ionic\Ionic\wollerosenkaufen\platforms\android\build\intermediates\pre-dexed\debug\classes-a0342bad41111596f8436cda33b5a5ee045fab74.jar" /> <dex dex="C:\Ionic\Ionic\wollerosenkaufen\platforms\android\build\intermediates\pre-dexed\debug\classes-a0342bad41111596f8436cda33b5a5ee045fab74.jar" />
</item> </item>
<item
jar="C:\Ionic\Ionic\git\wolle-rosen-kaufen\platforms\android\libs\sqlite-connector.jar"
jumboMode="false"
revision="22.0.1"
sha1="3193260c427164169c8e7f97cfc2bdd7c8efa18d">
<dex dex="C:\Ionic\Ionic\git\wolle-rosen-kaufen\platforms\android\build\intermediates\pre-dexed\debug\sqlite-connector-885e15ab34f1a9bfda8db7efc740bde597caea8c.jar" />
</item>
<item <item
jar="C:\Ionic\Ionic\git\wolle-rosen-kaufen\platforms\android\build\intermediates\exploded-aar\android\CordovaLib\unspecified\debug\classes.jar" jar="C:\Ionic\Ionic\git\wolle-rosen-kaufen\platforms\android\build\intermediates\exploded-aar\android\CordovaLib\unspecified\debug\classes.jar"
jumboMode="false" jumboMode="false"

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<merger version="3"><dataSet config="main"><source path="C:\Ionic\Ionic\wollerosenkaufen\platforms\android\src\androidTest\assets"/></dataSet></merger> <merger version="3"><dataSet config="main"><source path="C:\Ionic\Ionic\git\wolle-rosen-kaufen\platforms\android\src\androidTest\assets"/></dataSet></merger>

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<merger version="3"><dataSet config="main"><source path="C:\Ionic\Ionic\wollerosenkaufen\platforms\android\src\androidTest\res"/><source path="C:\Ionic\Ionic\wollerosenkaufen\platforms\android\build\generated\res\rs\test\debug"/><source path="C:\Ionic\Ionic\wollerosenkaufen\platforms\android\build\generated\res\generated\test\debug"/></dataSet><mergedItems/></merger> <merger version="3"><dataSet config="main"><source path="C:\Ionic\Ionic\git\wolle-rosen-kaufen\platforms\android\src\androidTest\res"/><source path="C:\Ionic\Ionic\git\wolle-rosen-kaufen\platforms\android\build\generated\res\rs\test\debug"/><source path="C:\Ionic\Ionic\git\wolle-rosen-kaufen\platforms\android\build\generated\res\generated\test\debug"/></dataSet><mergedItems/></merger>

View File

@ -33,6 +33,12 @@
<feature name="FileTransfer"> <feature name="FileTransfer">
<param name="android-package" value="org.apache.cordova.filetransfer.FileTransfer" /> <param name="android-package" value="org.apache.cordova.filetransfer.FileTransfer" />
</feature> </feature>
<feature name="SQLitePlugin">
<param name="android-package" value="io.liteglue.SQLitePlugin" />
</feature>
<feature name="sqlDB">
<param name="android-package" value="me.rahul.plugins.sqlDB.sqlDB" />
</feature>
<preference name="AndroidPersistentFileLocation" value="Internal" /> <preference name="AndroidPersistentFileLocation" value="Internal" />
<icon density="ldpi" src="resources/android/icon/drawable-ldpi-icon.png" /> <icon density="ldpi" src="resources/android/icon/drawable-ldpi-icon.png" />
<icon density="mdpi" src="resources/android/icon/drawable-mdpi-icon.png" /> <icon density="mdpi" src="resources/android/icon/drawable-mdpi-icon.png" />

View File

@ -1,2 +1,3 @@
C:\Ionic\Ionic\git\wolle-rosen-kaufen\platforms\android\build\intermediates\classes\debug C:\Ionic\Ionic\git\wolle-rosen-kaufen\platforms\android\build\intermediates\classes\debug
C:\Ionic\Ionic\git\wolle-rosen-kaufen\platforms\android\build\intermediates\pre-dexed\debug\sqlite-connector-885e15ab34f1a9bfda8db7efc740bde597caea8c.jar
C:\Ionic\Ionic\git\wolle-rosen-kaufen\platforms\android\build\intermediates\pre-dexed\debug\classes-ec055bf14dc687da3d1b2ed76b77548ddec8270d.jar C:\Ionic\Ionic\git\wolle-rosen-kaufen\platforms\android\build\intermediates\pre-dexed\debug\classes-ec055bf14dc687da3d1b2ed76b77548ddec8270d.jar

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -7,5 +7,5 @@
# Location of the SDK. This is only used by Gradle. # Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the # For customization when using a Version Control System, please read the
# header note. # header note.
#Thu Dec 17 20:46:38 CET 2015 #Fri Jan 15 00:58:01 CET 2016
sdk.dir=C\:\\Program Files (x86)\\Android\\android-sdk sdk.dir=C\:\\Program Files (x86)\\Android\\android-sdk

View File

@ -238,6 +238,22 @@ module.exports = [
"clobbers": [ "clobbers": [
"window.FileTransfer" "window.FileTransfer"
] ]
},
{
"file": "plugins/cordova-sqlite-storage/www/SQLitePlugin.js",
"id": "cordova-sqlite-storage.SQLitePlugin",
"pluginId": "cordova-sqlite-storage",
"clobbers": [
"SQLitePlugin"
]
},
{
"file": "plugins/me.rahul.plugins.sqlDB/www/sqlDB.js",
"id": "me.rahul.plugins.sqlDB.sqlDB",
"pluginId": "me.rahul.plugins.sqlDB",
"clobbers": [
"window.plugins.sqlDB"
]
} }
]; ];
module.exports.metadata = module.exports.metadata =
@ -252,7 +268,9 @@ module.exports.metadata =
"ionic-plugin-keyboard": "1.0.8", "ionic-plugin-keyboard": "1.0.8",
"cordova-plugin-inappbrowser": "1.1.1", "cordova-plugin-inappbrowser": "1.1.1",
"cordova-plugin-file": "3.0.0", "cordova-plugin-file": "3.0.0",
"cordova-plugin-file-transfer": "1.4.0" "cordova-plugin-file-transfer": "1.4.0",
"cordova-sqlite-storage": "0.7.15-pre",
"me.rahul.plugins.sqlDB": "1.0.3"
} }
// BOTTOM OF METADATA // BOTTOM OF METADATA
}); });

View File

@ -0,0 +1,576 @@
cordova.define("cordova-sqlite-storage.SQLitePlugin", function(require, exports, module) { (function() {
var DB_STATE_INIT, DB_STATE_OPEN, READ_ONLY_REGEX, SQLiteFactory, SQLitePlugin, SQLitePluginTransaction, argsArray, dblocations, newSQLError, nextTick, root, txLocks;
root = this;
READ_ONLY_REGEX = /^\s*(?:drop|delete|insert|update|create)\s/i;
DB_STATE_INIT = "INIT";
DB_STATE_OPEN = "OPEN";
txLocks = {};
newSQLError = function(error, code) {
var sqlError;
sqlError = error;
if (!code) {
code = 0;
}
if (!sqlError) {
sqlError = new Error("a plugin had an error but provided no response");
sqlError.code = code;
}
if (typeof sqlError === "string") {
sqlError = new Error(error);
sqlError.code = code;
}
if (!sqlError.code && sqlError.message) {
sqlError.code = code;
}
if (!sqlError.code && !sqlError.message) {
sqlError = new Error("an unknown error was returned: " + JSON.stringify(sqlError));
sqlError.code = code;
}
return sqlError;
};
nextTick = window.setImmediate || function(fun) {
window.setTimeout(fun, 0);
};
/*
Utility that avoids leaking the arguments object. See
https://www.npmjs.org/package/argsarray
*/
argsArray = function(fun) {
return function() {
var args, i, len;
len = arguments.length;
if (len) {
args = [];
i = -1;
while (++i < len) {
args[i] = arguments[i];
}
return fun.call(this, args);
} else {
return fun.call(this, []);
}
};
};
SQLitePlugin = function(openargs, openSuccess, openError) {
var dbname;
if (!(openargs && openargs['name'])) {
throw newSQLError("Cannot create a SQLitePlugin db instance without a db name");
}
dbname = openargs.name;
if (typeof dbname !== 'string') {
throw newSQLError('sqlite plugin database name must be a string');
}
this.openargs = openargs;
this.dbname = dbname;
this.openSuccess = openSuccess;
this.openError = openError;
this.openSuccess || (this.openSuccess = function() {
console.log("DB opened: " + dbname);
});
this.openError || (this.openError = function(e) {
console.log(e.message);
});
this.open(this.openSuccess, this.openError);
};
SQLitePlugin.prototype.databaseFeatures = {
isSQLitePluginDatabase: true
};
SQLitePlugin.prototype.openDBs = {};
SQLitePlugin.prototype.addTransaction = function(t) {
if (!txLocks[this.dbname]) {
txLocks[this.dbname] = {
queue: [],
inProgress: false
};
}
txLocks[this.dbname].queue.push(t);
if (this.dbname in this.openDBs && this.openDBs[this.dbname] !== DB_STATE_INIT) {
this.startNextTransaction();
} else {
if (this.dbname in this.openDBs) {
console.log('new transaction is waiting for open operation');
} else {
console.log('database is closed, new transaction is [stuck] waiting until db is opened again!');
}
}
};
SQLitePlugin.prototype.transaction = function(fn, error, success) {
if (!this.openDBs[this.dbname]) {
error(newSQLError('database not open'));
return;
}
this.addTransaction(new SQLitePluginTransaction(this, fn, error, success, true, false));
};
SQLitePlugin.prototype.readTransaction = function(fn, error, success) {
if (!this.openDBs[this.dbname]) {
error(newSQLError('database not open'));
return;
}
this.addTransaction(new SQLitePluginTransaction(this, fn, error, success, false, true));
};
SQLitePlugin.prototype.startNextTransaction = function() {
var self;
self = this;
nextTick((function(_this) {
return function() {
var txLock;
if (!(_this.dbname in _this.openDBs) || _this.openDBs[_this.dbname] !== DB_STATE_OPEN) {
console.log('cannot start next transaction: database not open');
return;
}
txLock = txLocks[self.dbname];
if (!txLock) {
console.log('cannot start next transaction: database connection is lost');
return;
} else if (txLock.queue.length > 0 && !txLock.inProgress) {
txLock.inProgress = true;
txLock.queue.shift().start();
}
};
})(this));
};
SQLitePlugin.prototype.abortAllPendingTransactions = function() {
var j, len1, ref, tx, txLock;
txLock = txLocks[this.dbname];
if (!!txLock && txLock.queue.length > 0) {
ref = txLock.queue;
for (j = 0, len1 = ref.length; j < len1; j++) {
tx = ref[j];
tx.abortFromQ(newSQLError('Invalid database handle'));
}
txLock.queue = [];
txLock.inProgress = false;
}
};
SQLitePlugin.prototype.open = function(success, error) {
var openerrorcb, opensuccesscb;
if (this.dbname in this.openDBs) {
console.log('database already open: ' + this.dbname);
nextTick((function(_this) {
return function() {
success(_this);
};
})(this));
} else {
console.log('OPEN database: ' + this.dbname);
opensuccesscb = (function(_this) {
return function() {
var txLock;
if (!_this.openDBs[_this.dbname]) {
console.log('database was closed during open operation');
}
if (_this.dbname in _this.openDBs) {
_this.openDBs[_this.dbname] = DB_STATE_OPEN;
}
if (!!success) {
success(_this);
}
txLock = txLocks[_this.dbname];
if (!!txLock && txLock.queue.length > 0 && !txLock.inProgress) {
_this.startNextTransaction();
}
};
})(this);
openerrorcb = (function(_this) {
return function() {
console.log('OPEN database: ' + _this.dbname + ' failed, aborting any pending transactions');
if (!!error) {
error(newSQLError('Could not open database'));
}
delete _this.openDBs[_this.dbname];
_this.abortAllPendingTransactions();
};
})(this);
this.openDBs[this.dbname] = DB_STATE_INIT;
cordova.exec(opensuccesscb, openerrorcb, "SQLitePlugin", "open", [this.openargs]);
}
};
SQLitePlugin.prototype.close = function(success, error) {
if (this.dbname in this.openDBs) {
if (txLocks[this.dbname] && txLocks[this.dbname].inProgress) {
console.log('cannot close: transaction is in progress');
error(newSQLError('database cannot be closed while a transaction is in progress'));
return;
}
console.log('CLOSE database: ' + this.dbname);
delete this.openDBs[this.dbname];
if (txLocks[this.dbname]) {
console.log('closing db with transaction queue length: ' + txLocks[this.dbname].queue.length);
} else {
console.log('closing db with no transaction lock state');
}
cordova.exec(success, error, "SQLitePlugin", "close", [
{
path: this.dbname
}
]);
} else {
console.log('cannot close: database is not open');
if (error) {
nextTick(function() {
return error();
});
}
}
};
SQLitePlugin.prototype.executeSql = function(statement, params, success, error) {
var myerror, myfn, mysuccess;
mysuccess = function(t, r) {
if (!!success) {
return success(r);
}
};
myerror = function(t, e) {
if (!!error) {
return error(e);
}
};
myfn = function(tx) {
tx.addStatement(statement, params, mysuccess, myerror);
};
this.addTransaction(new SQLitePluginTransaction(this, myfn, null, null, false, false));
};
SQLitePluginTransaction = function(db, fn, error, success, txlock, readOnly) {
if (typeof fn !== "function") {
/*
This is consistent with the implementation in Chrome -- it
throws if you pass anything other than a function. This also
prevents us from stalling our txQueue if somebody passes a
false value for fn.
*/
throw newSQLError("transaction expected a function");
}
this.db = db;
this.fn = fn;
this.error = error;
this.success = success;
this.txlock = txlock;
this.readOnly = readOnly;
this.executes = [];
if (txlock) {
this.addStatement("BEGIN", [], null, function(tx, err) {
throw newSQLError("unable to begin transaction: " + err.message, err.code);
});
}
};
SQLitePluginTransaction.prototype.start = function() {
var err, error1;
try {
this.fn(this);
this.run();
} catch (error1) {
err = error1;
txLocks[this.db.dbname].inProgress = false;
this.db.startNextTransaction();
if (this.error) {
this.error(newSQLError(err));
}
}
};
SQLitePluginTransaction.prototype.executeSql = function(sql, values, success, error) {
if (this.finalized) {
throw {
message: 'InvalidStateError: DOM Exception 11: This transaction is already finalized. Transactions are committed after its success or failure handlers are called. If you are using a Promise to handle callbacks, be aware that implementations following the A+ standard adhere to run-to-completion semantics and so Promise resolution occurs on a subsequent tick and therefore after the transaction commits.',
code: 11
};
return;
}
if (this.readOnly && READ_ONLY_REGEX.test(sql)) {
this.handleStatementFailure(error, {
message: 'invalid sql for a read-only transaction'
});
return;
}
this.addStatement(sql, values, success, error);
};
SQLitePluginTransaction.prototype.addStatement = function(sql, values, success, error) {
var j, len1, params, t, v;
params = [];
if (!!values && values.constructor === Array) {
for (j = 0, len1 = values.length; j < len1; j++) {
v = values[j];
t = typeof v;
params.push((v === null || v === void 0 || t === 'number' || t === 'string' ? v : v instanceof Blob ? v.valueOf() : v.toString()));
}
}
this.executes.push({
success: success,
error: error,
sql: sql,
params: params
});
};
SQLitePluginTransaction.prototype.handleStatementSuccess = function(handler, response) {
var payload, rows;
if (!handler) {
return;
}
rows = response.rows || [];
payload = {
rows: {
item: function(i) {
return rows[i];
},
length: rows.length
},
rowsAffected: response.rowsAffected || 0,
insertId: response.insertId || void 0
};
handler(this, payload);
};
SQLitePluginTransaction.prototype.handleStatementFailure = function(handler, response) {
if (!handler) {
throw newSQLError("a statement with no error handler failed: " + response.message, response.code);
}
if (handler(this, response) !== false) {
throw newSQLError("a statement error callback did not return false: " + response.message, response.code);
}
};
SQLitePluginTransaction.prototype.run = function() {
var batchExecutes, handlerFor, i, mycb, mycbmap, request, tropts, tx, txFailure, waiting;
txFailure = null;
tropts = [];
batchExecutes = this.executes;
waiting = batchExecutes.length;
this.executes = [];
tx = this;
handlerFor = function(index, didSucceed) {
return function(response) {
var err, error1;
try {
if (didSucceed) {
tx.handleStatementSuccess(batchExecutes[index].success, response);
} else {
tx.handleStatementFailure(batchExecutes[index].error, newSQLError(response));
}
} catch (error1) {
err = error1;
if (!txFailure) {
txFailure = newSQLError(err);
}
}
if (--waiting === 0) {
if (txFailure) {
tx.abort(txFailure);
} else if (tx.executes.length > 0) {
tx.run();
} else {
tx.finish();
}
}
};
};
i = 0;
mycbmap = {};
while (i < batchExecutes.length) {
request = batchExecutes[i];
mycbmap[i] = {
success: handlerFor(i, true),
error: handlerFor(i, false)
};
tropts.push({
qid: 1111,
sql: request.sql,
params: request.params
});
i++;
}
mycb = function(result) {
var j, last, q, r, ref, res, type;
last = result.length - 1;
for (i = j = 0, ref = last; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) {
r = result[i];
type = r.type;
res = r.result;
q = mycbmap[i];
if (q) {
if (q[type]) {
q[type](res);
}
}
}
};
cordova.exec(mycb, null, "SQLitePlugin", "backgroundExecuteSqlBatch", [
{
dbargs: {
dbname: this.db.dbname
},
executes: tropts
}
]);
};
SQLitePluginTransaction.prototype.abort = function(txFailure) {
var failed, succeeded, tx;
if (this.finalized) {
return;
}
tx = this;
succeeded = function(tx) {
txLocks[tx.db.dbname].inProgress = false;
tx.db.startNextTransaction();
if (tx.error) {
tx.error(txFailure);
}
};
failed = function(tx, err) {
txLocks[tx.db.dbname].inProgress = false;
tx.db.startNextTransaction();
if (tx.error) {
tx.error(newSQLError("error while trying to roll back: " + err.message, err.code));
}
};
this.finalized = true;
if (this.txlock) {
this.addStatement("ROLLBACK", [], succeeded, failed);
this.run();
} else {
succeeded(tx);
}
};
SQLitePluginTransaction.prototype.finish = function() {
var failed, succeeded, tx;
if (this.finalized) {
return;
}
tx = this;
succeeded = function(tx) {
txLocks[tx.db.dbname].inProgress = false;
tx.db.startNextTransaction();
if (tx.success) {
tx.success();
}
};
failed = function(tx, err) {
txLocks[tx.db.dbname].inProgress = false;
tx.db.startNextTransaction();
if (tx.error) {
tx.error(newSQLError("error while trying to commit: " + err.message, err.code));
}
};
this.finalized = true;
if (this.txlock) {
this.addStatement("COMMIT", [], succeeded, failed);
this.run();
} else {
succeeded(tx);
}
};
SQLitePluginTransaction.prototype.abortFromQ = function(sqlerror) {
if (this.error) {
this.error(sqlerror);
}
};
dblocations = ["docs", "libs", "nosync"];
SQLiteFactory = {
/*
NOTE: this function should NOT be translated from Javascript
back to CoffeeScript by js2coffee.
If this function is edited in Javascript then someone will
have to translate it back to CoffeeScript by hand.
*/
opendb: argsArray(function(args) {
var dblocation, errorcb, first, okcb, openargs;
if (args.length < 1) {
return null;
}
first = args[0];
openargs = null;
okcb = null;
errorcb = null;
if (first.constructor === String) {
openargs = {
name: first
};
if (args.length >= 5) {
okcb = args[4];
if (args.length > 5) {
errorcb = args[5];
}
}
} else {
openargs = first;
if (args.length >= 2) {
okcb = args[1];
if (args.length > 2) {
errorcb = args[2];
}
}
}
dblocation = !!openargs.location ? dblocations[openargs.location] : null;
openargs.dblocation = dblocation || dblocations[0];
if (!!openargs.createFromLocation && openargs.createFromLocation === 1) {
openargs.createFromResource = "1";
}
if (!!openargs.androidDatabaseImplementation && openargs.androidDatabaseImplementation === 2) {
openargs.androidOldDatabaseImplementation = 1;
}
if (!!openargs.androidLockWorkaround && openargs.androidLockWorkaround === 1) {
openargs.androidBugWorkaround = 1;
}
return new SQLitePlugin(openargs, okcb, errorcb);
}),
deleteDb: function(first, success, error) {
var args, dblocation;
args = {};
if (first.constructor === String) {
args.path = first;
args.dblocation = dblocations[0];
} else {
if (!(first && first['name'])) {
throw new Error("Please specify db name");
}
args.path = first.name;
dblocation = !!first.location ? dblocations[first.location] : null;
args.dblocation = dblocation || dblocations[0];
}
delete SQLitePlugin.prototype.openDBs[args.path];
return cordova.exec(success, error, "SQLitePlugin", "delete", [args]);
}
};
root.sqlitePlugin = {
sqliteFeatures: {
isSQLitePlugin: true
},
openDatabase: SQLiteFactory.opendb,
deleteDatabase: SQLiteFactory.deleteDb
};
}).call(this);
});

View File

@ -0,0 +1,11 @@
cordova.define("me.rahul.plugins.sqlDB.sqlDB", function(require, exports, module) { var exec = require('cordova/exec');
exports.copy = function(dbname, location, success, error) {
exec(success, error, "sqlDB", "copy", [dbname, location]);
};
exports.remove = function(dbname, location, success,error) {
exec(success, error, "sqlDB", "remove", [dbname, location]);
};
});

View File

@ -33,6 +33,12 @@
<feature name="FileTransfer"> <feature name="FileTransfer">
<param name="android-package" value="org.apache.cordova.filetransfer.FileTransfer" /> <param name="android-package" value="org.apache.cordova.filetransfer.FileTransfer" />
</feature> </feature>
<feature name="SQLitePlugin">
<param name="android-package" value="io.liteglue.SQLitePlugin" />
</feature>
<feature name="sqlDB">
<param name="android-package" value="me.rahul.plugins.sqlDB.sqlDB" />
</feature>
<preference name="AndroidPersistentFileLocation" value="Internal" /> <preference name="AndroidPersistentFileLocation" value="Internal" />
<icon density="ldpi" src="resources/android/icon/drawable-ldpi-icon.png" /> <icon density="ldpi" src="resources/android/icon/drawable-ldpi-icon.png" />
<icon density="mdpi" src="resources/android/icon/drawable-mdpi-icon.png" /> <icon density="mdpi" src="resources/android/icon/drawable-mdpi-icon.png" />

View File

@ -0,0 +1,509 @@
/*
* Copyright (c) 2012-2015: Christopher J. Brody (aka Chris Brody)
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010, IBM Corporation
*/
package io.liteglue;
import android.annotation.SuppressLint;
import android.database.Cursor;
import android.database.CursorWindow;
import android.database.sqlite.SQLiteCursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteStatement;
import android.util.Base64;
import android.util.Log;
import java.io.File;
import java.lang.IllegalArgumentException;
import java.lang.Number;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.cordova.CallbackContext;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Android Database helper class
*/
class SQLiteAndroidDatabase
{
private static final Pattern FIRST_WORD = Pattern.compile("^\\s*(\\S+)",
Pattern.CASE_INSENSITIVE);
private static final Pattern WHERE_CLAUSE = Pattern.compile("\\s+WHERE\\s+(.+)$",
Pattern.CASE_INSENSITIVE);
private static final Pattern UPDATE_TABLE_NAME = Pattern.compile("^\\s*UPDATE\\s+(\\S+)",
Pattern.CASE_INSENSITIVE);
private static final Pattern DELETE_TABLE_NAME = Pattern.compile("^\\s*DELETE\\s+FROM\\s+(\\S+)",
Pattern.CASE_INSENSITIVE);
File dbFile;
SQLiteDatabase mydb;
/**
* NOTE: Using default constructor, no explicit constructor.
*/
/**
* Open a database.
*
* @param dbfile The database File specification
*/
void open(File dbfile) throws Exception {
dbFile = dbfile; // for possible bug workaround
mydb = SQLiteDatabase.openOrCreateDatabase(dbfile, null);
}
/**
* Close a database (in the current thread).
*/
void closeDatabaseNow() {
if (mydb != null) {
mydb.close();
mydb = null;
}
}
void bugWorkaround() throws Exception {
this.closeDatabaseNow();
this.open(dbFile);
}
/**
* Executes a batch request and sends the results via cbc.
*
* @param dbname The name of the database.
* @param queryarr Array of query strings
* @param jsonparams Array of JSON query parameters
* @param queryIDs Array of query ids
* @param cbc Callback context from Cordova API
*/
@SuppressLint("NewApi")
void executeSqlBatch(String[] queryarr, JSONArray[] jsonparams,
String[] queryIDs, CallbackContext cbc) {
if (mydb == null) {
// not allowed - can only happen if someone has closed (and possibly deleted) a database and then re-used the database
cbc.error("database has been closed");
return;
}
String query = "";
String query_id = "";
int len = queryarr.length;
JSONArray batchResults = new JSONArray();
for (int i = 0; i < len; i++) {
int rowsAffectedCompat = 0;
boolean needRowsAffectedCompat = false;
query_id = queryIDs[i];
JSONObject queryResult = null;
String errorMessage = "unknown";
try {
boolean needRawQuery = true;
query = queryarr[i];
QueryType queryType = getQueryType(query);
if (queryType == QueryType.update || queryType == queryType.delete) {
if (android.os.Build.VERSION.SDK_INT >= 11) {
SQLiteStatement myStatement = mydb.compileStatement(query);
if (jsonparams != null) {
bindArgsToStatement(myStatement, jsonparams[i]);
}
int rowsAffected = -1; // (assuming invalid)
// Use try & catch just in case android.os.Build.VERSION.SDK_INT >= 11 is lying:
try {
rowsAffected = myStatement.executeUpdateDelete();
// Indicate valid results:
needRawQuery = false;
} catch (SQLiteException ex) {
// Indicate problem & stop this query:
ex.printStackTrace();
errorMessage = ex.getMessage();
Log.v("executeSqlBatch", "SQLiteStatement.executeUpdateDelete(): Error=" + errorMessage);
needRawQuery = false;
} catch (Exception ex) {
// Assuming SDK_INT was lying & method not found:
// do nothing here & try again with raw query.
}
if (rowsAffected != -1) {
queryResult = new JSONObject();
queryResult.put("rowsAffected", rowsAffected);
}
} else { // pre-honeycomb
rowsAffectedCompat = countRowsAffectedCompat(queryType, query, jsonparams, mydb, i);
needRowsAffectedCompat = true;
}
}
// INSERT:
if (queryType == QueryType.insert && jsonparams != null) {
needRawQuery = false;
SQLiteStatement myStatement = mydb.compileStatement(query);
bindArgsToStatement(myStatement, jsonparams[i]);
long insertId = -1; // (invalid)
try {
insertId = myStatement.executeInsert();
// statement has finished with no constraint violation:
queryResult = new JSONObject();
if (insertId != -1) {
queryResult.put("insertId", insertId);
queryResult.put("rowsAffected", 1);
} else {
queryResult.put("rowsAffected", 0);
}
} catch (SQLiteException ex) {
// report error result with the error message
// could be constraint violation or some other error
ex.printStackTrace();
errorMessage = ex.getMessage();
Log.v("executeSqlBatch", "SQLiteDatabase.executeInsert(): Error=" + errorMessage);
}
}
if (queryType == QueryType.begin) {
needRawQuery = false;
try {
mydb.beginTransaction();
queryResult = new JSONObject();
queryResult.put("rowsAffected", 0);
} catch (SQLiteException ex) {
ex.printStackTrace();
errorMessage = ex.getMessage();
Log.v("executeSqlBatch", "SQLiteDatabase.beginTransaction(): Error=" + errorMessage);
}
}
if (queryType == QueryType.commit) {
needRawQuery = false;
try {
mydb.setTransactionSuccessful();
mydb.endTransaction();
queryResult = new JSONObject();
queryResult.put("rowsAffected", 0);
} catch (SQLiteException ex) {
ex.printStackTrace();
errorMessage = ex.getMessage();
Log.v("executeSqlBatch", "SQLiteDatabase.setTransactionSuccessful/endTransaction(): Error=" + errorMessage);
}
}
if (queryType == QueryType.rollback) {
needRawQuery = false;
try {
mydb.endTransaction();
queryResult = new JSONObject();
queryResult.put("rowsAffected", 0);
} catch (SQLiteException ex) {
ex.printStackTrace();
errorMessage = ex.getMessage();
Log.v("executeSqlBatch", "SQLiteDatabase.endTransaction(): Error=" + errorMessage);
}
}
// raw query for other statements:
if (needRawQuery) {
queryResult = this.executeSqlStatementQuery(mydb, query, jsonparams[i], cbc);
if (needRowsAffectedCompat) {
queryResult.put("rowsAffected", rowsAffectedCompat);
}
}
} catch (Exception ex) {
ex.printStackTrace();
errorMessage = ex.getMessage();
Log.v("executeSqlBatch", "SQLiteAndroidDatabase.executeSql[Batch](): Error=" + errorMessage);
}
try {
if (queryResult != null) {
JSONObject r = new JSONObject();
r.put("qid", query_id);
r.put("type", "success");
r.put("result", queryResult);
batchResults.put(r);
} else {
JSONObject r = new JSONObject();
r.put("qid", query_id);
r.put("type", "error");
JSONObject er = new JSONObject();
er.put("message", errorMessage);
r.put("result", er);
batchResults.put(r);
}
} catch (JSONException ex) {
ex.printStackTrace();
Log.v("executeSqlBatch", "SQLiteAndroidDatabase.executeSql[Batch](): Error=" + ex.getMessage());
// TODO what to do?
}
}
cbc.success(batchResults);
}
private int countRowsAffectedCompat(QueryType queryType, String query, JSONArray[] jsonparams,
SQLiteDatabase mydb, int i) throws JSONException {
// quick and dirty way to calculate the rowsAffected in pre-Honeycomb. just do a SELECT
// beforehand using the same WHERE clause. might not be perfect, but it's better than nothing
Matcher whereMatcher = WHERE_CLAUSE.matcher(query);
String where = "";
int pos = 0;
while (whereMatcher.find(pos)) {
where = " WHERE " + whereMatcher.group(1);
pos = whereMatcher.start(1);
}
// WHERE clause may be omitted, and also be sure to find the last one,
// e.g. for cases where there's a subquery
// bindings may be in the update clause, so only take the last n
int numQuestionMarks = 0;
for (int j = 0; j < where.length(); j++) {
if (where.charAt(j) == '?') {
numQuestionMarks++;
}
}
JSONArray subParams = null;
if (jsonparams != null) {
// only take the last n of every array of sqlArgs
JSONArray origArray = jsonparams[i];
subParams = new JSONArray();
int startPos = origArray.length() - numQuestionMarks;
for (int j = startPos; j < origArray.length(); j++) {
subParams.put(j - startPos, origArray.get(j));
}
}
if (queryType == QueryType.update) {
Matcher tableMatcher = UPDATE_TABLE_NAME.matcher(query);
if (tableMatcher.find()) {
String table = tableMatcher.group(1);
try {
SQLiteStatement statement = mydb.compileStatement(
"SELECT count(*) FROM " + table + where);
if (subParams != null) {
bindArgsToStatement(statement, subParams);
}
return (int)statement.simpleQueryForLong();
} catch (Exception e) {
// assume we couldn't count for whatever reason, keep going
Log.e(SQLiteAndroidDatabase.class.getSimpleName(), "uncaught", e);
}
}
} else { // delete
Matcher tableMatcher = DELETE_TABLE_NAME.matcher(query);
if (tableMatcher.find()) {
String table = tableMatcher.group(1);
try {
SQLiteStatement statement = mydb.compileStatement(
"SELECT count(*) FROM " + table + where);
bindArgsToStatement(statement, subParams);
return (int)statement.simpleQueryForLong();
} catch (Exception e) {
// assume we couldn't count for whatever reason, keep going
Log.e(SQLiteAndroidDatabase.class.getSimpleName(), "uncaught", e);
}
}
}
return 0;
}
private void bindArgsToStatement(SQLiteStatement myStatement, JSONArray sqlArgs) throws JSONException {
for (int i = 0; i < sqlArgs.length(); i++) {
if (sqlArgs.get(i) instanceof Float || sqlArgs.get(i) instanceof Double) {
myStatement.bindDouble(i + 1, sqlArgs.getDouble(i));
} else if (sqlArgs.get(i) instanceof Number) {
myStatement.bindLong(i + 1, sqlArgs.getLong(i));
} else if (sqlArgs.isNull(i)) {
myStatement.bindNull(i + 1);
} else {
myStatement.bindString(i + 1, sqlArgs.getString(i));
}
}
}
/**
* Get rows results from query cursor.
*
* @param cur Cursor into query results
* @return results in string form
*/
private JSONObject executeSqlStatementQuery(SQLiteDatabase mydb,
String query, JSONArray paramsAsJson,
CallbackContext cbc) throws Exception {
JSONObject rowsResult = new JSONObject();
Cursor cur = null;
try {
String[] params = null;
params = new String[paramsAsJson.length()];
for (int j = 0; j < paramsAsJson.length(); j++) {
if (paramsAsJson.isNull(j))
params[j] = "";
else
params[j] = paramsAsJson.getString(j);
}
cur = mydb.rawQuery(query, params);
} catch (Exception ex) {
ex.printStackTrace();
String errorMessage = ex.getMessage();
Log.v("executeSqlBatch", "SQLiteAndroidDatabase.executeSql[Batch](): Error=" + errorMessage);
throw ex;
}
// If query result has rows
if (cur != null && cur.moveToFirst()) {
JSONArray rowsArrayResult = new JSONArray();
String key = "";
int colCount = cur.getColumnCount();
// Build up JSON result object for each row
do {
JSONObject row = new JSONObject();
try {
for (int i = 0; i < colCount; ++i) {
key = cur.getColumnName(i);
if (android.os.Build.VERSION.SDK_INT >= 11) {
// Use try & catch just in case android.os.Build.VERSION.SDK_INT >= 11 is lying:
try {
bindPostHoneycomb(row, key, cur, i);
} catch (Exception ex) {
bindPreHoneycomb(row, key, cur, i);
}
} else {
bindPreHoneycomb(row, key, cur, i);
}
}
rowsArrayResult.put(row);
} catch (JSONException e) {
e.printStackTrace();
}
} while (cur.moveToNext());
try {
rowsResult.put("rows", rowsArrayResult);
} catch (JSONException e) {
e.printStackTrace();
}
}
if (cur != null) {
cur.close();
}
return rowsResult;
}
@SuppressLint("NewApi")
private void bindPostHoneycomb(JSONObject row, String key, Cursor cur, int i) throws JSONException {
int curType = cur.getType(i);
switch (curType) {
case Cursor.FIELD_TYPE_NULL:
row.put(key, JSONObject.NULL);
break;
case Cursor.FIELD_TYPE_INTEGER:
row.put(key, cur.getLong(i));
break;
case Cursor.FIELD_TYPE_FLOAT:
row.put(key, cur.getDouble(i));
break;
case Cursor.FIELD_TYPE_BLOB:
row.put(key, new String(Base64.encode(cur.getBlob(i), Base64.DEFAULT)));
break;
case Cursor.FIELD_TYPE_STRING:
default: /* (not expected) */
row.put(key, cur.getString(i));
break;
}
}
private void bindPreHoneycomb(JSONObject row, String key, Cursor cursor, int i) throws JSONException {
// Since cursor.getType() is not available pre-honeycomb, this is
// a workaround so we don't have to bind everything as a string
// Details here: http://stackoverflow.com/q/11658239
SQLiteCursor sqLiteCursor = (SQLiteCursor) cursor;
CursorWindow cursorWindow = sqLiteCursor.getWindow();
int pos = cursor.getPosition();
if (cursorWindow.isNull(pos, i)) {
row.put(key, JSONObject.NULL);
} else if (cursorWindow.isLong(pos, i)) {
row.put(key, cursor.getLong(i));
} else if (cursorWindow.isFloat(pos, i)) {
row.put(key, cursor.getDouble(i));
} else if (cursorWindow.isBlob(pos, i)) {
row.put(key, new String(Base64.encode(cursor.getBlob(i), Base64.DEFAULT)));
} else { // string
row.put(key, cursor.getString(i));
}
}
static QueryType getQueryType(String query) {
Matcher matcher = FIRST_WORD.matcher(query);
if (matcher.find()) {
try {
return QueryType.valueOf(matcher.group(1).toLowerCase());
} catch (IllegalArgumentException ignore) {
// unknown verb
}
}
return QueryType.other;
}
static enum QueryType {
update,
insert,
delete,
select,
begin,
commit,
rollback,
other
}
} /* vim: set expandtab : */

View File

@ -0,0 +1,660 @@
/*
* Copyright (c) 2012-2015: Christopher J. Brody (aka Chris Brody)
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010, IBM Corporation
*/
package io.liteglue;
import android.annotation.SuppressLint;
import android.util.Base64;
import android.util.Log;
import java.io.File;
import java.lang.IllegalArgumentException;
import java.lang.Number;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
public class SQLitePlugin extends CordovaPlugin {
/**
* Multiple database runner map (static).
* NOTE: no public static accessor to db (runner) map since it would not work with db threading.
* FUTURE put DBRunner into a public class that can provide external accessor.
*/
static ConcurrentHashMap<String, DBRunner> dbrmap = new ConcurrentHashMap<String, DBRunner>();
/**
* SQLiteGlueConnector (instance of SQLiteConnector) for NDK version:
*/
static SQLiteConnector connector = new SQLiteConnector();
/**
* NOTE: Using default constructor, no explicit constructor.
*/
/**
* Executes the request and returns PluginResult.
*
* @param actionAsString The action to execute.
* @param args JSONArry of arguments for the plugin.
* @param cbc Callback context from Cordova API
* @return Whether the action was valid.
*/
@Override
public boolean execute(String actionAsString, JSONArray args, CallbackContext cbc) {
Action action;
try {
action = Action.valueOf(actionAsString);
} catch (IllegalArgumentException e) {
// shouldn't ever happen
Log.e(SQLitePlugin.class.getSimpleName(), "unexpected error", e);
return false;
}
try {
return executeAndPossiblyThrow(action, args, cbc);
} catch (JSONException e) {
// TODO: signal JSON problem to JS
Log.e(SQLitePlugin.class.getSimpleName(), "unexpected error", e);
return false;
}
}
private boolean executeAndPossiblyThrow(Action action, JSONArray args, CallbackContext cbc)
throws JSONException {
boolean status = true;
JSONObject o;
String dbname;
switch (action) {
case open:
o = args.getJSONObject(0);
dbname = o.getString("name");
// open database and start reading its queue
this.startDatabase(dbname, o, cbc);
break;
case close:
o = args.getJSONObject(0);
dbname = o.getString("path");
// put request in the q to close the db
this.closeDatabase(dbname, cbc);
break;
case delete:
o = args.getJSONObject(0);
dbname = o.getString("path");
deleteDatabase(dbname, cbc);
break;
case executeSqlBatch:
case backgroundExecuteSqlBatch:
String[] queries = null;
String[] queryIDs = null;
JSONArray jsonArr = null;
int paramLen = 0;
JSONArray[] jsonparams = null;
JSONObject allargs = args.getJSONObject(0);
JSONObject dbargs = allargs.getJSONObject("dbargs");
dbname = dbargs.getString("dbname");
JSONArray txargs = allargs.getJSONArray("executes");
if (txargs.isNull(0)) {
queries = new String[0];
} else {
int len = txargs.length();
queries = new String[len];
queryIDs = new String[len];
jsonparams = new JSONArray[len];
for (int i = 0; i < len; i++) {
JSONObject a = txargs.getJSONObject(i);
queries[i] = a.getString("sql");
queryIDs[i] = a.getString("qid");
jsonArr = a.getJSONArray("params");
paramLen = jsonArr.length();
jsonparams[i] = jsonArr;
}
}
// put db query in the queue to be executed in the db thread:
DBQuery q = new DBQuery(queries, queryIDs, jsonparams, cbc);
DBRunner r = dbrmap.get(dbname);
if (r != null) {
try {
r.q.put(q);
} catch(Exception e) {
Log.e(SQLitePlugin.class.getSimpleName(), "couldn't add to queue", e);
cbc.error("couldn't add to queue");
}
} else {
cbc.error("database not open");
}
break;
}
return status;
}
/**
* Clean up and close all open databases.
*/
@Override
public void onDestroy() {
while (!dbrmap.isEmpty()) {
String dbname = dbrmap.keySet().iterator().next();
this.closeDatabaseNow(dbname);
DBRunner r = dbrmap.get(dbname);
try {
// stop the db runner thread:
r.q.put(new DBQuery());
} catch(Exception e) {
Log.e(SQLitePlugin.class.getSimpleName(), "couldn't stop db thread", e);
}
dbrmap.remove(dbname);
}
}
// --------------------------------------------------------------------------
// LOCAL METHODS
// --------------------------------------------------------------------------
private void startDatabase(String dbname, JSONObject options, CallbackContext cbc) {
// TODO: is it an issue that we can orphan an existing thread? What should we do here?
// If we re-use the existing DBRunner it might be in the process of closing...
DBRunner r = dbrmap.get(dbname);
// Brody TODO: It may be better to terminate the existing db thread here & start a new one, instead.
if (r != null) {
// don't orphan the existing thread; just re-open the existing database.
// In the worst case it might be in the process of closing, but even that's less serious
// than orphaning the old DBRunner.
cbc.success();
} else {
r = new DBRunner(dbname, options, cbc);
dbrmap.put(dbname, r);
this.cordova.getThreadPool().execute(r);
}
}
/**
* Open a database.
*
* @param dbName The name of the database file
*/
private SQLiteAndroidDatabase openDatabase(String dbname, CallbackContext cbc, boolean old_impl) throws Exception {
try {
// ASSUMPTION: no db (connection/handle) is already stored in the map
// [should be true according to the code in DBRunner.run()]
File dbfile = this.cordova.getActivity().getDatabasePath(dbname);
if (!dbfile.exists()) {
dbfile.getParentFile().mkdirs();
}
Log.v("info", "Open sqlite db: " + dbfile.getAbsolutePath());
SQLiteAndroidDatabase mydb = old_impl ? new SQLiteAndroidDatabase() : new SQLiteDatabaseNDK();
mydb.open(dbfile);
if (cbc != null) // XXX Android locking/closing BUG workaround
cbc.success();
return mydb;
} catch (Exception e) {
if (cbc != null) // XXX Android locking/closing BUG workaround
cbc.error("can't open database " + e);
throw e;
}
}
/**
* Close a database (in another thread).
*
* @param dbName The name of the database file
*/
private void closeDatabase(String dbname, CallbackContext cbc) {
DBRunner r = dbrmap.get(dbname);
if (r != null) {
try {
r.q.put(new DBQuery(false, cbc));
} catch(Exception e) {
if (cbc != null) {
cbc.error("couldn't close database" + e);
}
Log.e(SQLitePlugin.class.getSimpleName(), "couldn't close database", e);
}
} else {
if (cbc != null) {
cbc.success();
}
}
}
/**
* Close a database (in the current thread).
*
* @param dbname The name of the database file
*/
private void closeDatabaseNow(String dbname) {
DBRunner r = dbrmap.get(dbname);
if (r != null) {
SQLiteAndroidDatabase mydb = r.mydb;
if (mydb != null)
mydb.closeDatabaseNow();
}
}
private void deleteDatabase(String dbname, CallbackContext cbc) {
DBRunner r = dbrmap.get(dbname);
if (r != null) {
try {
r.q.put(new DBQuery(true, cbc));
} catch(Exception e) {
if (cbc != null) {
cbc.error("couldn't close database" + e);
}
Log.e(SQLitePlugin.class.getSimpleName(), "couldn't close database", e);
}
} else {
boolean deleteResult = this.deleteDatabaseNow(dbname);
if (deleteResult) {
cbc.success();
} else {
cbc.error("couldn't delete database");
}
}
}
/**
* Delete a database.
*
* @param dbName The name of the database file
*
* @return true if successful or false if an exception was encountered
*/
private boolean deleteDatabaseNow(String dbname) {
File dbfile = this.cordova.getActivity().getDatabasePath(dbname);
try {
return cordova.getActivity().deleteDatabase(dbfile.getAbsolutePath());
} catch (Exception e) {
Log.e(SQLitePlugin.class.getSimpleName(), "couldn't delete database", e);
return false;
}
}
// NOTE: class hierarchy is ugly, done to reduce number of modules for manual installation.
// FUTURE TBD SQLiteDatabaseNDK class belongs in its own module.
class SQLiteDatabaseNDK extends SQLiteAndroidDatabase {
SQLiteConnection mydb;
/**
* Open a database.
*
* @param dbFile The database File specification
*/
@Override
void open(File dbFile) throws Exception {
mydb = connector.newSQLiteConnection(dbFile.getAbsolutePath(),
SQLiteOpenFlags.READWRITE | SQLiteOpenFlags.CREATE);
}
/**
* Close a database (in the current thread).
*/
@Override
void closeDatabaseNow() {
try {
if (mydb != null)
mydb.dispose();
} catch (Exception e) {
Log.e(SQLitePlugin.class.getSimpleName(), "couldn't close database, ignoring", e);
}
}
/**
* Ignore Android bug workaround for NDK version
*/
@Override
void bugWorkaround() { }
/**
* Executes a batch request and sends the results via cbc.
*
* @param dbname The name of the database.
* @param queryarr Array of query strings
* @param jsonparams Array of JSON query parameters
* @param queryIDs Array of query ids
* @param cbc Callback context from Cordova API
*/
@Override
void executeSqlBatch( String[] queryarr, JSONArray[] jsonparams,
String[] queryIDs, CallbackContext cbc) {
if (mydb == null) {
// not allowed - can only happen if someone has closed (and possibly deleted) a database and then re-used the database
cbc.error("database has been closed");
return;
}
int len = queryarr.length;
JSONArray batchResults = new JSONArray();
for (int i = 0; i < len; i++) {
int rowsAffectedCompat = 0;
boolean needRowsAffectedCompat = false;
String query_id = queryIDs[i];
JSONObject queryResult = null;
String errorMessage = "unknown";
try {
String query = queryarr[i];
long lastTotal = mydb.getTotalChanges();
queryResult = this.executeSqlStatementNDK(query, jsonparams[i], cbc);
long newTotal = mydb.getTotalChanges();
long rowsAffected = newTotal - lastTotal;
queryResult.put("rowsAffected", rowsAffected);
if (rowsAffected > 0) {
long insertId = mydb.getLastInsertRowid();
if (insertId > 0) {
queryResult.put("insertId", insertId);
}
}
} catch (Exception ex) {
ex.printStackTrace();
errorMessage = ex.getMessage();
Log.v("executeSqlBatch", "SQLitePlugin.executeSql[Batch](): Error=" + errorMessage);
}
try {
if (queryResult != null) {
JSONObject r = new JSONObject();
r.put("qid", query_id);
r.put("type", "success");
r.put("result", queryResult);
batchResults.put(r);
} else {
JSONObject r = new JSONObject();
r.put("qid", query_id);
r.put("type", "error");
JSONObject er = new JSONObject();
er.put("message", errorMessage);
r.put("result", er);
batchResults.put(r);
}
} catch (JSONException ex) {
ex.printStackTrace();
Log.v("executeSqlBatch", "SQLitePlugin.executeSql[Batch](): Error=" + ex.getMessage());
// TODO what to do?
}
}
cbc.success(batchResults);
}
/**
* Get rows results from query cursor.
*
* @param cur Cursor into query results
* @return results in string form
*/
private JSONObject executeSqlStatementNDK(String query, JSONArray paramsAsJson,
CallbackContext cbc) throws Exception {
JSONObject rowsResult = new JSONObject();
boolean hasRows = false;
SQLiteStatement myStatement = mydb.prepareStatement(query);
try {
String[] params = null;
params = new String[paramsAsJson.length()];
for (int i = 0; i < paramsAsJson.length(); ++i) {
if (paramsAsJson.isNull(i)) {
myStatement.bindNull(i + 1);
} else {
Object p = paramsAsJson.get(i);
if (p instanceof Float || p instanceof Double)
myStatement.bindDouble(i + 1, paramsAsJson.getDouble(i));
else if (p instanceof Number)
myStatement.bindLong(i + 1, paramsAsJson.getLong(i));
else
myStatement.bindTextNativeString(i + 1, paramsAsJson.getString(i));
}
}
hasRows = myStatement.step();
} catch (Exception ex) {
ex.printStackTrace();
String errorMessage = ex.getMessage();
Log.v("executeSqlBatch", "SQLitePlugin.executeSql[Batch](): Error=" + errorMessage);
// cleanup statement and throw the exception:
myStatement.dispose();
throw ex;
}
// If query result has rows
if (hasRows) {
JSONArray rowsArrayResult = new JSONArray();
String key = "";
int colCount = myStatement.getColumnCount();
// Build up JSON result object for each row
do {
JSONObject row = new JSONObject();
try {
for (int i = 0; i < colCount; ++i) {
key = myStatement.getColumnName(i);
switch (myStatement.getColumnType(i)) {
case SQLColumnType.NULL:
row.put(key, JSONObject.NULL);
break;
case SQLColumnType.REAL:
row.put(key, myStatement.getColumnDouble(i));
break;
case SQLColumnType.INTEGER:
row.put(key, myStatement.getColumnLong(i));
break;
case SQLColumnType.BLOB:
case SQLColumnType.TEXT:
default: // (just in case)
row.put(key, myStatement.getColumnTextNativeString(i));
}
}
rowsArrayResult.put(row);
} catch (JSONException e) {
e.printStackTrace();
}
} while (myStatement.step());
try {
rowsResult.put("rows", rowsArrayResult);
} catch (JSONException e) {
e.printStackTrace();
}
}
myStatement.dispose();
return rowsResult;
}
}
private class DBRunner implements Runnable {
final String dbname;
private boolean oldImpl;
private boolean bugWorkaround;
final BlockingQueue<DBQuery> q;
final CallbackContext openCbc;
SQLiteAndroidDatabase mydb;
DBRunner(final String dbname, JSONObject options, CallbackContext cbc) {
this.dbname = dbname;
this.oldImpl = options.has("androidOldDatabaseImplementation");
Log.v(SQLitePlugin.class.getSimpleName(), "Android db implementation: " + (oldImpl ? "OLD" : "default-NDK"));
this.bugWorkaround = this.oldImpl && options.has("androidBugWorkaround");
if (this.bugWorkaround)
Log.v(SQLitePlugin.class.getSimpleName(), "Android db closing/locking workaround applied");
this.q = new LinkedBlockingQueue<DBQuery>();
this.openCbc = cbc;
}
public void run() {
try {
this.mydb = openDatabase(dbname, this.openCbc, this.oldImpl);
} catch (Exception e) {
Log.e(SQLitePlugin.class.getSimpleName(), "unexpected error, stopping db thread", e);
dbrmap.remove(dbname);
return;
}
DBQuery dbq = null;
try {
dbq = q.take();
while (!dbq.stop) {
mydb.executeSqlBatch(dbq.queries, dbq.jsonparams, dbq.queryIDs, dbq.cbc);
// NOTE: androidLock[Bug]Workaround is not necessary and IGNORED for default-NDK version.
if (this.bugWorkaround && dbq.queries.length == 1 && dbq.queries[0] == "COMMIT")
mydb.bugWorkaround();
dbq = q.take();
}
} catch (Exception e) {
Log.e(SQLitePlugin.class.getSimpleName(), "unexpected error", e);
}
if (dbq != null && dbq.close) {
try {
closeDatabaseNow(dbname);
dbrmap.remove(dbname); // (should) remove ourself
if (!dbq.delete) {
dbq.cbc.success();
} else {
try {
boolean deleteResult = deleteDatabaseNow(dbname);
if (deleteResult) {
dbq.cbc.success();
} else {
dbq.cbc.error("couldn't delete database");
}
} catch (Exception e) {
Log.e(SQLitePlugin.class.getSimpleName(), "couldn't delete database", e);
dbq.cbc.error("couldn't delete database: " + e);
}
}
} catch (Exception e) {
Log.e(SQLitePlugin.class.getSimpleName(), "couldn't close database", e);
if (dbq.cbc != null) {
dbq.cbc.error("couldn't close database: " + e);
}
}
}
}
}
private final class DBQuery {
// XXX TODO replace with DBRunner action enum:
final boolean stop;
final boolean close;
final boolean delete;
final String[] queries;
final String[] queryIDs;
final JSONArray[] jsonparams;
final CallbackContext cbc;
DBQuery(String[] myqueries, String[] qids, JSONArray[] params, CallbackContext c) {
this.stop = false;
this.close = false;
this.delete = false;
this.queries = myqueries;
this.queryIDs = qids;
this.jsonparams = params;
this.cbc = c;
}
DBQuery(boolean delete, CallbackContext cbc) {
this.stop = true;
this.close = true;
this.delete = delete;
this.queries = null;
this.queryIDs = null;
this.jsonparams = null;
this.cbc = cbc;
}
// signal the DBRunner thread to stop:
DBQuery() {
this.stop = true;
this.close = false;
this.delete = false;
this.queries = null;
this.queryIDs = null;
this.jsonparams = null;
this.cbc = null;
}
}
private static enum Action {
open,
close,
delete,
executeSqlBatch,
backgroundExecuteSqlBatch,
}
}
/* vim: set expandtab : */

View File

@ -0,0 +1,63 @@
package me.rahul.plugins.sqlDB;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DatabaseHelper extends SQLiteOpenHelper {
private Context myContext;
public DatabaseHelper(Context context) {
super(context, sqlDB.dbname, null, 1);
this.myContext = context;
// TODO Auto-generated constructor stub
}
public void createdatabase(File dbPath) throws IOException {
// Log.d("CordovaLog","Inside CreateDatabase = "+dbPath);
this.getReadableDatabase();
try {
copyDatabase(dbPath);
} catch (IOException e) {
throw new Error(
"Create Database Exception ============================ "
+ e);
}
}
private void copyDatabase(File database) throws IOException {
InputStream myInput = myContext.getAssets().open("www/"+sqlDB.dbname);
OutputStream myOutput = new FileOutputStream(database);
byte[] buffer = new byte[1024];
while ((myInput.read(buffer)) > -1) {
myOutput.write(buffer);
}
myOutput.flush();
myOutput.close();
myInput.close();
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
}
}

View File

@ -0,0 +1,102 @@
package me.rahul.plugins.sqlDB;
import java.io.File;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
/**
* This class echoes a string called from JavaScript.
*/
public class sqlDB extends CordovaPlugin {
public static String dbname = "dbname";
PluginResult plresult = new PluginResult(PluginResult.Status.NO_RESULT);
@Override
public boolean execute(String action, JSONArray args,
CallbackContext callbackContext) throws JSONException {
if (action.equalsIgnoreCase("copy")) {
this.copyDB(args.getString(0), callbackContext);
return true;
} else if (action.equalsIgnoreCase("remove")) {
String db = args.getString(0);
File path = cordova.getActivity().getDatabasePath(db);
Boolean fileExists = path.exists();
if (fileExists) {
boolean deleted = path.delete();
if (deleted) {
plresult = new PluginResult(PluginResult.Status.OK, deleted);
callbackContext.sendPluginResult(plresult);
} else {
plresult = new PluginResult(PluginResult.Status.ERROR,
deleted);
callbackContext.sendPluginResult(plresult);
}
} else {
plresult = new PluginResult(PluginResult.Status.ERROR,
"File Doesn't Exists");
callbackContext.sendPluginResult(plresult);
}
return true;
} else {
plresult = new PluginResult(PluginResult.Status.INVALID_ACTION);
callbackContext.sendPluginResult(plresult);
return false;
}
}
private void copyDB(String dbName, final CallbackContext callbackContext) {
final File dbpath;
dbname = dbName;
JSONObject error = new JSONObject();
final DatabaseHelper dbhelper = new DatabaseHelper(this.cordova
.getActivity().getApplicationContext());
dbpath = this.cordova.getActivity().getDatabasePath(dbname);
Boolean dbexists = dbpath.exists();
//Log.d("CordovaLog", "DatabasePath = " + dbpath + "&&&& dbname = " + dbname + "&&&&DB Exists =" + dbexists);
if (dbexists) {
try {
error.put("message", "File already exists");
error.put("code", 516);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
plresult = new PluginResult(PluginResult.Status.ERROR, error);
callbackContext.sendPluginResult(plresult);
} else {
cordova.getThreadPool().execute(new Runnable() {
@Override
public void run() {
PluginResult plResult = new PluginResult(
PluginResult.Status.NO_RESULT);
// TODO Auto-generated method stub
try {
dbhelper.createdatabase(dbpath);
plResult = new PluginResult(PluginResult.Status.OK);
callbackContext.sendPluginResult(plResult);
} catch (Exception e) {
plResult = new PluginResult(PluginResult.Status.ERROR,
e.getMessage());
callbackContext.sendPluginResult(plResult);
}
}
});
}
}
}

View File

@ -37,6 +37,12 @@
}, },
"cordova-plugin-file-transfer": { "cordova-plugin-file-transfer": {
"PACKAGE_NAME": "com.raataar.wolle_rosen_kaufen" "PACKAGE_NAME": "com.raataar.wolle_rosen_kaufen"
},
"cordova-sqlite-storage": {
"PACKAGE_NAME": "com.raataar.wolle_rosen_kaufen"
},
"me.rahul.plugins.sqlDB": {
"PACKAGE_NAME": "com.raataar.wolle_rosen_kaufen"
} }
}, },
"dependent_plugins": {} "dependent_plugins": {}

View File

@ -0,0 +1,35 @@
# Authors
## Common Javascript
- Extracted from DroidGap by @brodybits (Christopher J. Brody aka Chris Brody)
- Fail-safe nested transaction support by @ef4 (Edward Faulkner)
- Translated to Coffee-Script using js2coffee tool by @brodybits (Chris Brody)
- API changes by @brodybits (Chris Brody)
- Transaction timing fixes to support PouchDB by @nolanlawson
## Android version
- Extracted from DroidGap by @brodybits (Chris Brody)
- Transaction batch processing of Android version by @marcucio
- Maintained by @brodybits (Chris Brody)
- Fixes to support old Android versions by @nolanlawson
- Thanks to Mark Oppenheim <mark.oppenheim@mnetics.co.uk> for fixes to open/close callbacks and repeated open/close/delete operations
## iOS version
- Original authors: @davibe (Davide Bertola <dade@dadeb.it>) and @joenoon (Joe Noon <joenoon@gmail.com>)
- Cordova 2.7+ port with background processing by @j3k0 (Jean-Christophe Hoelt <hoelt@fovea.cc>)
- Maintained by @brodybits (Chris Brody)
## Windows (8.1) version
- SQLiteProxy.js by @vldmrrr (Vladimir Avdonin) and @brodybits (Chris Brody)
- SQLite3-WinRT C++ classes and SQLite3JS (Javascript part) by @doo (doo GmbH)
## WP(7/8) version
- Original author: @marcucio (Mike Arcucio <mike@marcucio.com>)
- Enhancements for background processing & improved transaction support by @Gillardo (Darren Gillard <darren.gillard81@gmail.com>)
- DB threading and open/close/delete fixes by Mark Oppenheim <mark.oppenheim@mnetics.co.uk>
- Uses csharp-sqlite library by Noah Hart and others (MIT license)

View File

@ -0,0 +1,125 @@
# Changes
## 0.7.15-pre
- All iOS operations are now using background processing (reported to resolve intermittent problems with cordova-ios@4.0.1)
## 0.7.14
- REGEXP support removed from this version branch
- Remove src/android/libs/.gitignore (inadvertently added in 0.7.13)
## 0.7.13
- REGEXP support removed from this version branch
- Rename Windows C++ Database close function to closedb to resolve conflict for Windows Store certification
- Android version with sqlite `3.8.10.2` embedded (with error messages fixed)
- Pre-populated database support removed from this version branch
- Amazon Fire-OS support removed
- Fix conversion warnings in iOS version
## 0.7.12
- Fix to Windows "Universal" version to support big integers
- Implement database close and delete operations for Windows "Universal"
- Fix readTransaction to skip BEGIN/COMMIT/ROLLBACK
## 0.7.11
- Fix plugin ID in plugin.xml to match npm package ID
- Unpacked sqlite-native-driver.so libraries from jar
- Fix conversion of INTEGER type (iOS version)
- Disable code to read BLOB as Base-64 due to https://issues.apache.org/jira/browse/CB-9638
## 0.7.10
- Use Android-sqlite-connector instead of sqlite4java
## 0.7.9
- Build iOS and Windows versions with sqlite 3.8.10.2 embedded
- Fix plugin id to match npm package id
## 0.7.8
- Support FTS3/FTS4 and R-Tree in iOS and Windows "Universal" (8.1) versions
- Build ARM target with Function Level Linking ref: http://www.monkey-x.com/Community/posts.php?topic=7739
- SQLite3.Windows.vcxproj and SQLite3.WindowsPhone.vcxproj in their own directories to avoid problems due to temporary files
## 0.7.7
- include build of sqlite4java for Android x86_64 and arm-64
- clean publish to plugins.cordova.io
## 0.7.6
- Small fix to plugin id
- Disable use of gethostuuid() in sqlite3.c (only used in iOS version)
- published to plugins.cordova.io - [BUG] published extra junk in workarea, causing problems with Windows (Universal) version
## 0.7.5
- Windows (Universal) version now supports both Windows 8.1 and Windows Phone 8.1
- iOS and Windows versions are now built with sqlite 3.8.9 embedded
- Improved locking style and other optimizations applied for iOS version
## 0.7.4
- iOS and Windows (8.1) versions built to keep non-essential temporary sqlite files in memory
- Option to use legacy Android database library, with Android locking/closing issue (BUG #193) workaround included again
## 0.7.3
- insertId & rowsAffected implemented for Windows (8.1)
- plugin id changed
## 0.7.2
- Android version with sqlite4java (sqlite 3.8.7 embedded), which solves BUG #193: Android closing/locking issue (ICU-UNICODE integration is now missing)
- iOS version fixed to override the correct pluginInitialize method and built with sqlite 3.8.8.3 embedded
## 0.7.1
- Project renamed
- Initial version for Windows (8.1) [with sqlite 3.8.8.3 embedded]
- Abort initially pending transactions for db handle (due to incorrect password key, for example) [from Cordova-sqlcipher-storage]
- WP7 build enabled (NOT TESTED)
## 1.0.6
- Proper handling of transactions that may be requested before the database open operation is completed
- Report an error upon attempt to close a database handle object multiple times.
## 1.0.5
- Workaround for Android db locking/closing issue
- Fix double-precision REAL values in result (iOS version)
- Fix query result truncation in case of NULL character (\0 or \u0000) (iOS version)
- Convert array SQL parameters to string, according to match Web SQL spec
- Fix closing of Android database
- Some fixes for SQL API error handling to be consistent with Web SQL
## 1.0.4
- Pre-populated database option (Android/iOS)
- Option to select database location to disable iCloud backup (iOS ONLY)
- Safeguard against closing of database while transaction is pending
- Fix to prevent double marshaling of data
## 1.0.3
- Fixed issue with multi-page apps on Android (due to problem when closing & re-opening app)
## 1.0.2
- Workaround for issue with multiple UPDATE statements WP(8) (#128)
## 1.0.1
- Support Cordova 3.3.0/3.4.0 to support Amazon-FireOS
- Fixes for WP(8):
- use one thread per db to solve open/close/delete issues
- fix integer data binding
- Fix open/close callbacks Android & WP(8)
- Resolve issue with INSERT OR IGNORE (Android)

View File

@ -0,0 +1,44 @@
# Licenses
## Common Javascript
MIT or Apache 2.0
## Android version
MIT or Apache 2.0
## iOS version
MIT only
## Windows (8.1) version
MIT or Apache 2.0
### SQLite3-WinRT [used by Windows (8.1) version]
by @doo (doo GmbH)
## WP(7/8) version
MIT or Apache 2.0
## Class lib(s) used by WP(7/8) version
### C#-SQLite
MIT License
C#-SQLite is an independent reimplementation of the SQLite software library.
SQLite® is a registered trademark of Hipp, Wyrick & Company, Inc.
The overall C#-SQLite package is distributed under the terms of the MIT License, given below. Some parts are distributed under more permissive licenses, see individual source files for details.
Copyright (c) 2009-2012 Noah Hart and others, see individual source files for details.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,198 @@
Lawnchair.adapter('webkit-sqlite', (function () {
// private methods
var fail = function (e, i) { console.log('error in sqlite adaptor!', e, i) }
, now = function () { return new Date() } // FIXME need to use better date fn
// not entirely sure if this is needed...
if (!Function.prototype.bind) {
Function.prototype.bind = function( obj ) {
var slice = [].slice
, args = slice.call(arguments, 1)
, self = this
, nop = function () {}
, bound = function () {
return self.apply(this instanceof nop ? this : (obj || {}), args.concat(slice.call(arguments)))
}
nop.prototype = self.prototype
bound.prototype = new nop()
return bound
}
}
// public methods
return {
valid: function() { return !!(sqlitePlugin.openDatabase) },
init: function (options, callback) {
var that = this
, cb = that.fn(that.name, callback)
, dbname = options.db || this.name
, bgType = options.bgType || 1
, create = "CREATE TABLE IF NOT EXISTS " + this.name + " (id NVARCHAR(32) UNIQUE PRIMARY KEY, value TEXT, timestamp REAL)"
, win = function(){ return cb.call(that, that); }
// open a connection and create the db if it doesn't exist
this.db = sqlitePlugin.openDatabase({name:dbname,bgType:bgType})
this.db.transaction(function (t) {
t.executeSql(create, [], win, fail)
})
},
keys: function (callback) {
var cb = this.lambda(callback)
, that = this
, keys = "SELECT id FROM " + this.name + " ORDER BY timestamp DESC"
this.db.transaction(function(t) {
var win = function (xxx, results) {
if (results.rows.length == 0 ) {
cb.call(that, [])
} else {
var r = [];
for (var i = 0, l = results.rows.length; i < l; i++) {
r.push(results.rows.item(i).id);
}
cb.call(that, r)
}
}
t.executeSql(keys, [], win, fail)
})
return this
},
// you think thats air you're breathing now?
save: function (obj, callback) {
var that = this
, id = obj.key || that.uuid()
, ins = "INSERT INTO " + this.name + " (value, timestamp, id) VALUES (?,?,?)"
, up = "UPDATE " + this.name + " SET value=?, timestamp=? WHERE id=?"
, win = function () { if (callback) { obj.key = id; that.lambda(callback).call(that, obj) }}
, val = [now(), id]
// existential
that.exists(obj.key, function(exists) {
// transactions are like condoms
that.db.transaction(function(t) {
// TODO move timestamp to a plugin
var insert = function (obj) {
val.unshift(JSON.stringify(obj))
t.executeSql(ins, val, win, fail)
}
// TODO move timestamp to a plugin
var update = function (obj) {
delete(obj.key)
val.unshift(JSON.stringify(obj))
t.executeSql(up, val, win, fail)
}
// pretty
exists ? update(obj) : insert(obj)
})
});
return this
},
// FIXME this should be a batch insert / just getting the test to pass...
batch: function (objs, cb) {
var results = []
, done = false
, that = this
var updateProgress = function(obj) {
results.push(obj)
done = results.length === objs.length
}
var checkProgress = setInterval(function() {
if (done) {
if (cb) that.lambda(cb).call(that, results)
clearInterval(checkProgress)
}
}, 200)
for (var i = 0, l = objs.length; i < l; i++)
this.save(objs[i], updateProgress)
return this
},
get: function (keyOrArray, cb) {
var that = this
, sql = ''
// batch selects support
if (this.isArray(keyOrArray)) {
sql = 'SELECT id, value FROM ' + this.name + " WHERE id IN ('" + keyOrArray.join("','") + "')"
} else {
sql = 'SELECT id, value FROM ' + this.name + " WHERE id = '" + keyOrArray + "'"
}
// FIXME
// will always loop the results but cleans it up if not a batch return at the end..
// in other words, this could be faster
var win = function (xxx, results) {
var o = null
, r = []
if (results.rows.length) {
for (var i = 0, l = results.rows.length; i < l; i++) {
o = JSON.parse(results.rows.item(i).value)
o.key = results.rows.item(i).id
r.push(o)
}
}
if (!that.isArray(keyOrArray)) r = r.length ? r[0] : null
if (cb) that.lambda(cb).call(that, r)
}
this.db.transaction(function(t){ t.executeSql(sql, [], win, fail) })
return this
},
exists: function (key, cb) {
var is = "SELECT * FROM " + this.name + " WHERE id = ?"
, that = this
, win = function(xxx, results) { if (cb) that.fn('exists', cb).call(that, (results.rows.length > 0)) }
this.db.transaction(function(t){ t.executeSql(is, [key], win, fail) })
return this
},
all: function (callback) {
var that = this
, all = "SELECT * FROM " + this.name
, r = []
, cb = this.fn(this.name, callback) || undefined
, win = function (xxx, results) {
if (results.rows.length != 0) {
for (var i = 0, l = results.rows.length; i < l; i++) {
var obj = JSON.parse(results.rows.item(i).value)
obj.key = results.rows.item(i).id
r.push(obj)
}
}
if (cb) cb.call(that, r)
}
this.db.transaction(function (t) {
t.executeSql(all, [], win, fail)
})
return this
},
remove: function (keyOrObj, cb) {
var that = this
, key = typeof keyOrObj === 'string' ? keyOrObj : keyOrObj.key
, del = "DELETE FROM " + this.name + " WHERE id = ?"
, win = function () { if (cb) that.lambda(cb).call(that) }
this.db.transaction( function (t) {
t.executeSql(del, [key], win, fail);
});
return this;
},
nuke: function (cb) {
var nuke = "DELETE FROM " + this.name
, that = this
, win = cb ? function() { that.lambda(cb).call(that) } : function(){}
this.db.transaction(function (t) {
t.executeSql(nuke, [], win, fail)
})
return this
}
//////
}})())

View File

@ -0,0 +1,198 @@
Lawnchair.adapter('webkit-sqlite', (function () {
// private methods
var fail = function (e, i) { console.log('error in sqlite adaptor!', e, i) }
, now = function () { return new Date() } // FIXME need to use better date fn
// not entirely sure if this is needed...
if (!Function.prototype.bind) {
Function.prototype.bind = function( obj ) {
var slice = [].slice
, args = slice.call(arguments, 1)
, self = this
, nop = function () {}
, bound = function () {
return self.apply(this instanceof nop ? this : (obj || {}), args.concat(slice.call(arguments)))
}
nop.prototype = self.prototype
bound.prototype = new nop()
return bound
}
}
// public methods
return {
valid: function() { return !!(sqlitePlugin.openDatabase) },
init: function (options, callback) {
var that = this
, cb = that.fn(that.name, callback)
, dbname = options.db || this.name
, bgType = options.bgType || 1
, create = "CREATE TABLE IF NOT EXISTS " + this.name + " (id NVARCHAR(32) UNIQUE PRIMARY KEY, value TEXT, timestamp REAL)"
, win = function(){ return cb.call(that, that); }
// open a connection and create the db if it doesn't exist
this.db = sqlitePlugin.openDatabase({name:dbname,bgType:bgType})
this.db.transaction(function (t) {
t.executeSql(create, [], win, fail)
})
},
keys: function (callback) {
var cb = this.lambda(callback)
, that = this
, keys = "SELECT id FROM " + this.name + " ORDER BY timestamp DESC"
this.db.transaction(function(t) {
var win = function (xxx, results) {
if (results.rows.length == 0 ) {
cb.call(that, [])
} else {
var r = [];
for (var i = 0, l = results.rows.length; i < l; i++) {
r.push(results.rows.item(i).id);
}
cb.call(that, r)
}
}
t.executeSql(keys, [], win, fail)
})
return this
},
// you think thats air you're breathing now?
save: function (obj, callback) {
var that = this
, id = obj.key || that.uuid()
, ins = "INSERT INTO " + this.name + " (value, timestamp, id) VALUES (?,?,?)"
, up = "UPDATE " + this.name + " SET value=?, timestamp=? WHERE id=?"
, win = function () { if (callback) { obj.key = id; that.lambda(callback).call(that, obj) }}
, val = [now(), id]
// existential
that.exists(obj.key, function(exists) {
// transactions are like condoms
that.db.transaction(function(t) {
// TODO move timestamp to a plugin
var insert = function (obj) {
val.unshift(JSON.stringify(obj))
t.executeSql(ins, val, win, fail)
}
// TODO move timestamp to a plugin
var update = function (obj) {
delete(obj.key)
val.unshift(JSON.stringify(obj))
t.executeSql(up, val, win, fail)
}
// pretty
exists ? update(obj) : insert(obj)
})
});
return this
},
// FIXME this should be a batch insert / just getting the test to pass...
batch: function (objs, cb) {
var results = []
, done = false
, that = this
var updateProgress = function(obj) {
results.push(obj)
done = results.length === objs.length
}
var checkProgress = setInterval(function() {
if (done) {
if (cb) that.lambda(cb).call(that, results)
clearInterval(checkProgress)
}
}, 200)
for (var i = 0, l = objs.length; i < l; i++)
this.save(objs[i], updateProgress)
return this
},
get: function (keyOrArray, cb) {
var that = this
, sql = ''
// batch selects support
if (this.isArray(keyOrArray)) {
sql = 'SELECT id, value FROM ' + this.name + " WHERE id IN ('" + keyOrArray.join("','") + "')"
} else {
sql = 'SELECT id, value FROM ' + this.name + " WHERE id = '" + keyOrArray + "'"
}
// FIXME
// will always loop the results but cleans it up if not a batch return at the end..
// in other words, this could be faster
var win = function (xxx, results) {
var o = null
, r = []
if (results.rows.length) {
for (var i = 0, l = results.rows.length; i < l; i++) {
o = JSON.parse(results.rows.item(i).value)
o.key = results.rows.item(i).id
r.push(o)
}
}
if (!that.isArray(keyOrArray)) r = r.length ? r[0] : null
if (cb) that.lambda(cb).call(that, r)
}
this.db.transaction(function(t){ t.executeSql(sql, [], win, fail) })
return this
},
exists: function (key, cb) {
var is = "SELECT * FROM " + this.name + " WHERE id = ?"
, that = this
, win = function(xxx, results) { if (cb) that.fn('exists', cb).call(that, (results.rows.length > 0)) }
this.db.transaction(function(t){ t.executeSql(is, [key], win, fail) })
return this
},
all: function (callback) {
var that = this
, all = "SELECT * FROM " + this.name
, r = []
, cb = this.fn(this.name, callback) || undefined
, win = function (xxx, results) {
if (results.rows.length != 0) {
for (var i = 0, l = results.rows.length; i < l; i++) {
var obj = JSON.parse(results.rows.item(i).value)
obj.key = results.rows.item(i).id
r.push(obj)
}
}
if (cb) cb.call(that, r)
}
this.db.transaction(function (t) {
t.executeSql(all, [], win, fail)
})
return this
},
remove: function (keyOrObj, cb) {
var that = this
, key = typeof keyOrObj === 'string' ? keyOrObj : keyOrObj.key
, del = "DELETE FROM " + this.name + " WHERE id = ?"
, win = function () { if (cb) that.lambda(cb).call(that) }
this.db.transaction( function (t) {
t.executeSql(del, [key], win, fail);
});
return this;
},
nuke: function (cb) {
var nuke = "DELETE FROM " + this.name
, that = this
, win = cb ? function() { that.lambda(cb).call(that) } : function(){}
this.db.transaction(function (t) {
t.executeSql(nuke, [], win, fail)
})
return this
}
//////
}})())

View File

@ -0,0 +1,76 @@
<!DOCTYPE html>
<html>
<head>
<title>Lawnchair Spec</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no;" />
<meta charset="utf-8">
<link rel="stylesheet" href="lib/qunit.css" type="text/css" media="screen">
<!-- If your application is targeting iOS BEFORE 4.0 you MUST put json2.js from http://www.JSON.org/json2.js into your www directory and include it here -->
<script type="text/javascript" charset="utf-8" src="cordova-1.5.0.js"></script>
<script src="lib/qunit.js"></script>
<script src="lib/json2.js"></script>
<script src="lib/lawnchair.js"></script>
<script src="SQLitePlugin.js"></script>
<script src="Lawnchair-sqlitePlugin.js"></script>
<script src="lawnchair-spec.js"></script>
<script type="text/javascript">
// autostart seems to be a little eager
QUnit.config.autostart = false
// kill qunit saves
try{ sessionStorage.clear(); } catch(e){}
function onBodyLoad()
{
document.addEventListener("deviceready", startTests, false);
}
var store;
function startTests() {
// allow restriction of adapter in URL (e.g., ?adapter=ie-userdata)
var allowed = /adapter=([-\w]+)/.exec(window.location.href),
adapters = Lawnchair.adapters,
i;
if (allowed) {
for (i = 0; i < adapters.length; ) {
if (adapters[i].adapter == allowed[1]) {
i++;
} else {
adapters.splice(i, 1)
}
}
if (adapters.length == 0) {
alert("no such adapter: " + allowed[1]);
}
if (!adapters[0].valid()) {
alert("adapter " + allowed[1] + " is not valid in this environment");
}
}
// kickup the chair
store = new Lawnchair({name:'tests'}, function(){
QUnit.start()
})
}
</script>
</head>
<body onload="onBodyLoad()">
<h1 id="qunit-header">Lawnchair Spec</h1>
<h2 id="qunit-banner"></h2>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
</body>
</html>

View File

@ -0,0 +1,431 @@
module('Lawnchair construction/destruction', {
setup:function() {
},
teardown:function() {
}
});
test('ctor requires callbacks in each form', function() {
QUnit.stop();
QUnit.expect(6);
// raise exception if no ctor callback is supplied
try {
var lc2 = new Lawnchair();
} catch(e) {
ok(true, 'exception raised if no callback supplied to init');
}
try {
var lc3 = new Lawnchair({}, {});
} catch(e) {
ok(true, 'exception raised if no callback supplied to init, but two args are present');
}
try {
var lc3 = new Lawnchair({});
} catch(e) {
ok(true, 'exception raised if no callback supplied to init, but one arg is present');
}
var lc = new Lawnchair({name:store.name}, function(ref) {
ok(true, 'should call passed in callback when using obj+function ctor form')
equals(this, ref, "lawnchair callback scoped to lawnchair instance")
equals(ref, this, "lawnchair passes self into callback too")
QUnit.start()
});
});
/** NOTE: may cause a failure due to difference in SQLitePlugin database initialization.
test('independent data stores', function() {
var store1 = new Lawnchair({name: "store1"}, function() {});
store1 .save({key: 'apple', quantity: 3}, function() {
var store2 = new Lawnchair({name: "store2"}, function() {});
store1.all(function(r) {
equals(r.length, 1);
});
store2.all(function(r) {
equals(r.length, 0);
});
})
})
**/
module('all()', {
setup:function() {
QUnit.stop();
// I like to make all my variables globals. Starting a new trend.
me = {name:'brian', age:30};
store.nuke(function() { QUnit.start(); });
},
teardown:function() {
me = null;
}
})
test('chainable', function() {
QUnit.stop();
QUnit.expect(1);
same(store.all(function(r) { QUnit.start(); }), store, 'should be chainable (return itself)');
})
test('full callback syntax', function() {
QUnit.stop();
QUnit.expect(4);
store.all(function(r) {
ok(true, 'calls callback');
ok(r instanceof Array, 'should provide array as parameter');
equals(r.length, 0, 'parameter should initially have zero length');
same(this, store, '"this" should be scoped to the lawnchair object inside callback');
QUnit.start();
});
})
test('adding, nuking and size tests', function() {
QUnit.stop();
QUnit.expect(2);
store.save(me, function() {
store.all(function(r) {
equals(r.length, 1, 'parameter should have length 1 after saving a single record');
store.nuke(function() {
store.all(function(r) {
equals(r.length, 0, 'parameter should have length 0 after nuking');
QUnit.start();
});
});
});
});
})
test( 'shorthand callback syntax', function() {
QUnit.stop();
QUnit.expect(2);
store.all('ok(true, "shorthand syntax callback gets evaled"); same(this, store, "`this` should be scoped to the Lawnchair instance"); QUnit.start();');
// Is this test block necessary?
//
// var tmp = new Lawnchair({name:'temps', record:'tmp'}, function(){
// QUnit.start()
// var Temps = this;
// equals(this, Temps, 'this is bound to Lawnchair')
// QUnit.stop()
// Temps.all('ok(temps, "this.name is passed to all callback"); QUnit.start()')
// })
})
/** TBD issue with Android:
test('scoped variable in shorthand callback', function() {
QUnit.expect(1);
QUnit.stop();
// FIXME fkn qunit being weird here... expect(1)
var tmp = new Lawnchair({name:'temps', record:'tmp'}, function() {
this.nuke(function() {
this.save({a:1}, function() {
this.each('ok(tmp, "this.record is passed to each callback"); QUnit.start()')
})
})
})
})
**/
module('nuke()', {
setup:function() {
QUnit.stop();
store.nuke(function() {
QUnit.start()
});
},
teardown:function() {
}
})
test( 'chainable', function() {
QUnit.expect(1);
QUnit.stop()
same(store.nuke(function() { QUnit.start() }), store, 'should be chainable');
})
test( 'full callback syntax', function() {
QUnit.stop();
QUnit.expect(2);
store.nuke(function() {
ok(true, "should call callback in nuke");
same(this, store, '"this" should be scoped to the Lawnchair instance');
QUnit.start();
});
})
test( 'shorthand callback syntax', function() {
QUnit.stop();
QUnit.expect(2);
store.nuke('ok(true, "shorthand syntax callback gets evaled"); same(this, store, "`this` should be scoped to the Lawnchair instance"); QUnit.start();');
})
module('save()', {
setup:function() {
QUnit.stop();
// I like to make all my variables globals. Starting a new trend.
me = {name:'brian', age:30};
store.nuke(function() { QUnit.start(); });
},
teardown:function() {
me = null;
}
})
test( 'chainable', function() {
QUnit.stop();
QUnit.expect(1);
same(store.save(me, function() { QUnit.start(); }), store, 'should be chainable');
})
test( 'full callback syntax', function() {
QUnit.stop();
QUnit.expect(2);
store.save(me, function(it) {
ok(true, 'should call passed in callback');
same(it, me, 'should pass in original saved object in callback');
QUnit.start();
});
})
test( 'shorthand callback syntax', function() {
QUnit.stop();
QUnit.expect(2);
store.save(me, 'ok(true, "shorthand syntax callback gets evaled"); same(this, store, "`this` should be scoped to the Lawnchair instance"); QUnit.start();');
})
test( 'saving objects', function() {
QUnit.stop();
QUnit.expect(1);
store.save(me, function() {
store.save({key:"something", value:"else"}, function(r) {
store.all(function(r) {
equals(r.length, 2, 'after saving two keys, num. records should equal to 2');
QUnit.start();
});
});
})
})
test( 'save without callback', function() {
QUnit.stop();
QUnit.expect(1);
store.save(me, function(obj) {
var key = obj.key;
store.save(obj);
equals(obj.key, key, "save without callback retains key");
QUnit.start();
})
});
module('batch()', {
setup:function() {
QUnit.stop();
// I like to make all my variables globals. Starting a new trend.
me = {name:'brian', age:30};
store.nuke(function() { QUnit.start(); });
},
teardown:function() {
me = null;
}
})
test('batch insertion', function(){
QUnit.expect(3);
QUnit.stop();
ok(store.batch, 'batch implemented');
equals(store.batch([]), store, 'chainable')
store.batch([{i:1},{i:2}], function() {
store.all(function(r){
equals(r.length, 2, 'should be two records from batch insert with array of two objects');
QUnit.start();
});
});
})
test( 'full callback syntax', function() {
QUnit.stop(1500); // timing changed by batch processing improvements
QUnit.expect(2);
store.batch([{j:'k'}], function() {
ok(true, 'callback called with full syntax');
same(this, store, '"this" should be the LAwnchair instance');
QUnit.start();
})
})
test( 'shorthand callback syntax', function() {
QUnit.stop(1500); // timing changed by batch processing improvements
QUnit.expect(2);
store.batch([{o:'k'}], 'ok(true, "shorthand syntax callback gets evaled"); same(this, store, "`this` should be scoped to the Lawnchair instance"); QUnit.start();')
})
module('get()', {
setup:function() {
QUnit.stop();
// I like to make all my variables globals. Starting a new trend.
me = {name:'brian', age:30};
store.nuke(function() { QUnit.start(); });
},
teardown:function() {
me = null;
}
});
test( 'should it be chainable?', function() {
QUnit.expect(1);
QUnit.stop();
equals(store.get('foo', function() { QUnit.start(); }), store, 'get chainable');
});
test('get functionality', function() {
QUnit.expect(4);
QUnit.stop();
store.save({key:'xyz', name:'tim'}, function() {
store.get('xyz', function(r) {
equals(r.key, 'xyz', 'should return key in loaded object');
equals(r.name, 'tim', 'should return proper object when calling get with a key');
store.get('doesntexist', function(s) {
ok(true, 'should call callback even for non-existent key');
equals(s, null, 'should return null for non-existent key');
QUnit.start();
});
});
});
});
test('get batch functionality', function() {
QUnit.expect(3);
QUnit.stop(1500); // timing changed by batch processing improvements
var t = [{key:'test-get'},{key:'test-get-1'}]
store.batch(t, function() {
this.get(['test-get','test-get-1'], function(r) {
equals(r[0].key, 'test-get', "get first object");
equals(r[1].key, 'test-get-1', "get second object");
equals(r.length, t.length, "should batch get")
QUnit.start()
})
})
});
test( 'full callback syntax', function() {
QUnit.stop();
QUnit.expect(2);
store.get('somekey', function(r){
ok(true, 'callback got called');
same(this, store, '"this" should be teh Lawnchair instance');
QUnit.start();
});
});
test('short callback syntax', function() {
QUnit.stop();
QUnit.expect(2);
store.get('somekey', 'ok(true, "shorthand syntax callback gets evaled"); same(this, store, "`this` should be scoped to the Lawnchair instance"); QUnit.start();');
});
module('remove()', {
setup:function() {
QUnit.stop();
// I like to make all my variables globals. Starting a new trend.
me = {name:'brian', age:30};
store.nuke(function() { QUnit.start(); });
},
teardown:function() {
me = null;
}
});
test( 'chainable', function() {
QUnit.expect(1);
QUnit.stop();
store.save({key:'me', name:'brian'}, function() {
same(store.remove('me', function() {
QUnit.start();
}), store, 'should be chainable');
});
});
test( 'full callback syntax', function() {
QUnit.stop();
QUnit.expect(2);
store.save({key:'somekey', name:'something'}, function() {
store.remove('somekey', function(r){
ok(true, 'callback got called');
same(this, store, '"this" should be teh Lawnchair instance');
QUnit.start();
});
});
});
test('short callback syntax', function() {
QUnit.stop();
QUnit.expect(2);
store.save({key:'somekey', name:'something'}, function() {
store.remove('somekey', 'ok(true, "shorthand syntax callback gets evaled"); same(this, store, "`this` should be scoped to the Lawnchair instance"); QUnit.start();');
});
});
// FIXME need to add tests for batch deletion
test( 'remove functionality', function() {
QUnit.stop();
QUnit.expect(2);
store.save({name:'joni'}, function(r) {
//store.find("r.name == 'joni'", function(r){
store.remove(r, function(r) {
store.all(function(all) {
equals(all.length, 0, "should have length 0 after saving, finding, and removing a record using entire object");
store.save({key:'die', name:'dudeman'}, function(r) {
store.remove('die', function(r){
store.all(function(rec) {
equals(rec.length, 0, "should have length 0 after saving and removing by string key");
QUnit.start();
});
});
});
});
});
//});
});
});

View File

@ -0,0 +1,482 @@
/*
http://www.JSON.org/json2.js
2010-03-20
Public Domain.
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
See http://www.JSON.org/js.html
This code should be minified before deployment.
See http://javascript.crockford.com/jsmin.html
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
NOT CONTROL.
This file creates a global JSON object containing two methods: stringify
and parse.
JSON.stringify(value, replacer, space)
value any JavaScript value, usually an object or array.
replacer an optional parameter that determines how object
values are stringified for objects. It can be a
function or an array of strings.
space an optional parameter that specifies the indentation
of nested structures. If it is omitted, the text will
be packed without extra whitespace. If it is a number,
it will specify the number of spaces to indent at each
level. If it is a string (such as '\t' or '&nbsp;'),
it contains the characters used to indent at each level.
This method produces a JSON text from a JavaScript value.
When an object value is found, if the object contains a toJSON
method, its toJSON method will be called and the result will be
stringified. A toJSON method does not serialize: it returns the
value represented by the name/value pair that should be serialized,
or undefined if nothing should be serialized. The toJSON method
will be passed the key associated with the value, and this will be
bound to the value
For example, this would serialize Dates as ISO strings.
Date.prototype.toJSON = function (key) {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
return this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z';
};
You can provide an optional replacer method. It will be passed the
key and value of each member, with this bound to the containing
object. The value that is returned from your method will be
serialized. If your method returns undefined, then the member will
be excluded from the serialization.
If the replacer parameter is an array of strings, then it will be
used to select the members to be serialized. It filters the results
such that only members with keys listed in the replacer array are
stringified.
Values that do not have JSON representations, such as undefined or
functions, will not be serialized. Such values in objects will be
dropped; in arrays they will be replaced with null. You can use
a replacer function to replace those with JSON values.
JSON.stringify(undefined) returns undefined.
The optional space parameter produces a stringification of the
value that is filled with line breaks and indentation to make it
easier to read.
If the space parameter is a non-empty string, then that string will
be used for indentation. If the space parameter is a number, then
the indentation will be that many spaces.
Example:
text = JSON.stringify(['e', {pluribus: 'unum'}]);
// text is '["e",{"pluribus":"unum"}]'
text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
text = JSON.stringify([new Date()], function (key, value) {
return this[key] instanceof Date ?
'Date(' + this[key] + ')' : value;
});
// text is '["Date(---current time---)"]'
JSON.parse(text, reviver)
This method parses a JSON text to produce an object or array.
It can throw a SyntaxError exception.
The optional reviver parameter is a function that can filter and
transform the results. It receives each of the keys and values,
and its return value is used instead of the original value.
If it returns what it received, then the structure is not modified.
If it returns undefined then the member is deleted.
Example:
// Parse the text. Values that look like ISO date strings will
// be converted to Date objects.
myData = JSON.parse(text, function (key, value) {
var a;
if (typeof value === 'string') {
a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
if (a) {
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+a[5], +a[6]));
}
}
return value;
});
myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
var d;
if (typeof value === 'string' &&
value.slice(0, 5) === 'Date(' &&
value.slice(-1) === ')') {
d = new Date(value.slice(5, -1));
if (d) {
return d;
}
}
return value;
});
This is a reference implementation. You are free to copy, modify, or
redistribute.
*/
/*jslint evil: true, strict: false */
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
lastIndex, length, parse, prototype, push, replace, slice, stringify,
test, toJSON, toString, valueOf
*/
// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.
if (!this.JSON) {
this.JSON = {};
}
(function () {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
if (typeof Date.prototype.toJSON !== 'function') {
Date.prototype.toJSON = function (key) {
return isFinite(this.valueOf()) ?
this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z' : null;
};
String.prototype.toJSON =
Number.prototype.toJSON =
Boolean.prototype.toJSON = function (key) {
return this.valueOf();
};
}
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
gap,
indent,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
},
rep;
function quote(string) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
escapable.lastIndex = 0;
return escapable.test(string) ?
'"' + string.replace(escapable, function (a) {
var c = meta[a];
return typeof c === 'string' ? c :
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"' :
'"' + string + '"';
}
function str(key, holder) {
// Produce a string from holder[key].
var i, // The loop counter.
k, // The member key.
v, // The member value.
length,
mind = gap,
partial,
value = holder[key];
// If the value has a toJSON method, call it to obtain a replacement value.
if (value && typeof value === 'object' &&
typeof value.toJSON === 'function') {
value = value.toJSON(key);
}
// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.
if (typeof rep === 'function') {
value = rep.call(holder, key, value);
}
// What happens next depends on the value's type.
switch (typeof value) {
case 'string':
return quote(value);
case 'number':
// JSON numbers must be finite. Encode non-finite numbers as null.
return isFinite(value) ? String(value) : 'null';
case 'boolean':
case 'null':
// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.
return String(value);
// If the type is 'object', we might be dealing with an object or an array or
// null.
case 'object':
// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.
if (!value) {
return 'null';
}
// Make an array to hold the partial results of stringifying this object value.
gap += indent;
partial = [];
// Is the value an array?
if (Object.prototype.toString.apply(value) === '[object Array]') {
// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.
length = value.length;
for (i = 0; i < length; i += 1) {
partial[i] = str(i, value) || 'null';
}
// Join all of the elements together, separated with commas, and wrap them in
// brackets.
v = partial.length === 0 ? '[]' :
gap ? '[\n' + gap +
partial.join(',\n' + gap) + '\n' +
mind + ']' :
'[' + partial.join(',') + ']';
gap = mind;
return v;
}
// If the replacer is an array, use it to select the members to be stringified.
if (rep && typeof rep === 'object') {
length = rep.length;
for (i = 0; i < length; i += 1) {
k = rep[i];
if (typeof k === 'string') {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
} else {
// Otherwise, iterate through all of the keys in the object.
for (k in value) {
if (Object.hasOwnProperty.call(value, k)) {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
}
// Join all of the member texts together, separated with commas,
// and wrap them in braces.
v = partial.length === 0 ? '{}' :
gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
mind + '}' : '{' + partial.join(',') + '}';
gap = mind;
return v;
}
}
// If the JSON object does not yet have a stringify method, give it one.
if (typeof JSON.stringify !== 'function') {
JSON.stringify = function (value, replacer, space) {
// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.
var i;
gap = '';
indent = '';
// If the space parameter is a number, make an indent string containing that
// many spaces.
if (typeof space === 'number') {
for (i = 0; i < space; i += 1) {
indent += ' ';
}
// If the space parameter is a string, it will be used as the indent string.
} else if (typeof space === 'string') {
indent = space;
}
// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.
rep = replacer;
if (replacer && typeof replacer !== 'function' &&
(typeof replacer !== 'object' ||
typeof replacer.length !== 'number')) {
throw new Error('JSON.stringify');
}
// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.
return str('', {'': value});
};
}
// If the JSON object does not yet have a parse method, give it one.
if (typeof JSON.parse !== 'function') {
JSON.parse = function (text, reviver) {
// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.
var j;
function walk(holder, key) {
// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.
var k, v, value = holder[key];
if (value && typeof value === 'object') {
for (k in value) {
if (Object.hasOwnProperty.call(value, k)) {
v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return reviver.call(holder, key, value);
}
// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.
text = String(text);
cx.lastIndex = 0;
if (cx.test(text)) {
text = text.replace(cx, function (a) {
return '\\u' +
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
});
}
// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.
// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
if (/^[\],:{}\s]*$/.
test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.
j = eval('(' + text + ')');
// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.
return typeof reviver === 'function' ?
walk({'': j}, '') : j;
}
// If the text is not JSON parseable, then a SyntaxError is thrown.
throw new SyntaxError('JSON.parse');
};
}
}());

View File

@ -0,0 +1,151 @@
/**
* Lawnchair!
* ---
* clientside json store
*
*/
var Lawnchair = function (options, callback) {
// ensure Lawnchair was called as a constructor
if (!(this instanceof Lawnchair)) return new Lawnchair(options, callback);
// lawnchair requires json
if (!JSON) throw 'JSON unavailable! Include http://www.json.org/json2.js to fix.'
// options are optional; callback is not
if (arguments.length <= 2 && arguments.length > 0) {
callback = (typeof arguments[0] === 'function') ? arguments[0] : arguments[1];
options = (typeof arguments[0] === 'function') ? {} : arguments[0];
} else {
throw 'Incorrect # of ctor args!'
}
// TODO perhaps allow for pub/sub instead?
if (typeof callback !== 'function') throw 'No callback was provided';
// default configuration
this.record = options.record || 'record' // default for records
this.name = options.name || 'records' // default name for underlying store
// mixin first valid adapter
var adapter
// if the adapter is passed in we try to load that only
if (options.adapter) {
for (var i = 0, l = Lawnchair.adapters.length; i < l; i++) {
if (Lawnchair.adapters[i].adapter === options.adapter) {
adapter = Lawnchair.adapters[i].valid() ? Lawnchair.adapters[i] : undefined;
break;
}
}
// otherwise find the first valid adapter for this env
}
else {
for (var i = 0, l = Lawnchair.adapters.length; i < l; i++) {
adapter = Lawnchair.adapters[i].valid() ? Lawnchair.adapters[i] : undefined
if (adapter) break
}
}
// we have failed
if (!adapter) throw 'No valid adapter.'
// yay! mixin the adapter
for (var j in adapter)
this[j] = adapter[j]
// call init for each mixed in plugin
for (var i = 0, l = Lawnchair.plugins.length; i < l; i++)
Lawnchair.plugins[i].call(this)
// init the adapter
this.init(options, callback)
}
Lawnchair.adapters = []
/**
* queues an adapter for mixin
* ===
* - ensures an adapter conforms to a specific interface
*
*/
Lawnchair.adapter = function (id, obj) {
// add the adapter id to the adapter obj
// ugly here for a cleaner dsl for implementing adapters
obj['adapter'] = id
// methods required to implement a lawnchair adapter
var implementing = 'adapter valid init keys save batch get exists all remove nuke'.split(' ')
, indexOf = this.prototype.indexOf
// mix in the adapter
for (var i in obj) {
if (indexOf(implementing, i) === -1) throw 'Invalid adapter! Nonstandard method: ' + i
}
// if we made it this far the adapter interface is valid
// insert the new adapter as the preferred adapter
Lawnchair.adapters.splice(0,0,obj)
}
Lawnchair.plugins = []
/**
* generic shallow extension for plugins
* ===
* - if an init method is found it registers it to be called when the lawnchair is inited
* - yes we could use hasOwnProp but nobody here is an asshole
*/
Lawnchair.plugin = function (obj) {
for (var i in obj)
i === 'init' ? Lawnchair.plugins.push(obj[i]) : this.prototype[i] = obj[i]
}
/**
* helpers
*
*/
Lawnchair.prototype = {
isArray: Array.isArray || function(o) { return Object.prototype.toString.call(o) === '[object Array]' },
/**
* this code exists for ie8... for more background see:
* http://www.flickr.com/photos/westcoastlogic/5955365742/in/photostream
*/
indexOf: function(ary, item, i, l) {
if (ary.indexOf) return ary.indexOf(item)
for (i = 0, l = ary.length; i < l; i++) if (ary[i] === item) return i
return -1
},
// awesome shorthand callbacks as strings. this is shameless theft from dojo.
lambda: function (callback) {
return this.fn(this.record, callback)
},
// first stab at named parameters for terse callbacks; dojo: first != best // ;D
fn: function (name, callback) {
return typeof callback == 'string' ? new Function(name, callback) : callback
},
// returns a unique identifier (by way of Backbone.localStorage.js)
// TODO investigate smaller UUIDs to cut on storage cost
uuid: function () {
var S4 = function () {
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
}
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
},
// a classic iterator
each: function (callback) {
var cb = this.lambda(callback)
// iterate from chain
if (this.__results) {
for (var i = 0, l = this.__results.length; i < l; i++) cb.call(this, this.__results[i], i)
}
// otherwise iterate the entire collection
else {
this.all(function(r) {
for (var i = 0, l = r.length; i < l; i++) cb.call(this, r[i], i)
})
}
return this
}
// --
};

View File

@ -0,0 +1,225 @@
/**
* QUnit - A JavaScript Unit Testing Framework
*
* http://docs.jquery.com/QUnit
*
* Copyright (c) 2011 John Resig, Jörn Zaefferer
* Dual licensed under the MIT (MIT-LICENSE.txt)
* or GPL (GPL-LICENSE.txt) licenses.
*/
/** Font Family and Sizes */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
}
#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
#qunit-tests { font-size: smaller; }
/** Resets */
#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {
margin: 0;
padding: 0;
}
/** Header */
#qunit-header {
padding: 0.5em 0 0.5em 1em;
color: #8699a4;
background-color: #0d3349;
font-size: 1.5em;
line-height: 1em;
font-weight: normal;
border-radius: 15px 15px 0 0;
-moz-border-radius: 15px 15px 0 0;
-webkit-border-top-right-radius: 15px;
-webkit-border-top-left-radius: 15px;
}
#qunit-header a {
text-decoration: none;
color: #c2ccd1;
}
#qunit-header a:hover,
#qunit-header a:focus {
color: #fff;
}
#qunit-banner {
height: 5px;
}
#qunit-testrunner-toolbar {
padding: 0.5em 0 0.5em 2em;
color: #5E740B;
background-color: #eee;
}
#qunit-userAgent {
padding: 0.5em 0 0.5em 2.5em;
background-color: #2b81af;
color: #fff;
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
}
/** Tests: Pass/Fail */
#qunit-tests {
list-style-position: inside;
}
#qunit-tests li {
padding: 0.4em 0.5em 0.4em 2.5em;
border-bottom: 1px solid #fff;
list-style-position: inside;
}
#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
display: none;
}
#qunit-tests li strong {
cursor: pointer;
}
#qunit-tests li a {
padding: 0.5em;
color: #c2ccd1;
text-decoration: none;
}
#qunit-tests li a:hover,
#qunit-tests li a:focus {
color: #000;
}
#qunit-tests ol {
margin-top: 0.5em;
padding: 0.5em;
background-color: #fff;
border-radius: 15px;
-moz-border-radius: 15px;
-webkit-border-radius: 15px;
box-shadow: inset 0px 2px 13px #999;
-moz-box-shadow: inset 0px 2px 13px #999;
-webkit-box-shadow: inset 0px 2px 13px #999;
}
#qunit-tests table {
border-collapse: collapse;
margin-top: .2em;
}
#qunit-tests th {
text-align: right;
vertical-align: top;
padding: 0 .5em 0 0;
}
#qunit-tests td {
vertical-align: top;
}
#qunit-tests pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
#qunit-tests del {
background-color: #e0f2be;
color: #374e0c;
text-decoration: none;
}
#qunit-tests ins {
background-color: #ffcaca;
color: #500;
text-decoration: none;
}
/*** Test Counts */
#qunit-tests b.counts { color: black; }
#qunit-tests b.passed { color: #5E740B; }
#qunit-tests b.failed { color: #710909; }
#qunit-tests li li {
margin: 0.5em;
padding: 0.4em 0.5em 0.4em 0.5em;
background-color: #fff;
border-bottom: none;
list-style-position: inside;
}
/*** Passing Styles */
#qunit-tests li li.pass {
color: #5E740B;
background-color: #fff;
border-left: 26px solid #C6E746;
}
#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
#qunit-tests .pass .test-name { color: #366097; }
#qunit-tests .pass .test-actual,
#qunit-tests .pass .test-expected { color: #999999; }
#qunit-banner.qunit-pass { background-color: #C6E746; }
/*** Failing Styles */
#qunit-tests li li.fail {
color: #710909;
background-color: #fff;
border-left: 26px solid #EE5757;
}
#qunit-tests > li:last-child {
border-radius: 0 0 15px 15px;
-moz-border-radius: 0 0 15px 15px;
-webkit-border-bottom-right-radius: 15px;
-webkit-border-bottom-left-radius: 15px;
}
#qunit-tests .fail { color: #000000; background-color: #EE5757; }
#qunit-tests .fail .test-name,
#qunit-tests .fail .module-name { color: #000000; }
#qunit-tests .fail .test-actual { color: #EE5757; }
#qunit-tests .fail .test-expected { color: green; }
#qunit-banner.qunit-fail { background-color: #EE5757; }
/** Result */
#qunit-testresult {
padding: 0.5em 0.5em 0.5em 2.5em;
color: #2b81af;
background-color: #D2E0E6;
border-bottom: 1px solid white;
}
/** Fixture */
#qunit-fixture {
position: absolute;
top: -10000px;
left: -10000px;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,957 @@
# Cordova/PhoneGap sqlite storage adapter
Native interface to sqlite in a Cordova/PhoneGap plugin for Android, iOS, Windows "Universal" (8.1), and WP(7/8) with API similar to HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/).
License for Android, Windows "Universal" (8.1), and WP(7/8) versions: MIT or Apache 2.0
License for iOS version: MIT only
|Android CI (~~full~~ limited suite)|iOS CI (limited suite)|
|-----------------------|----------------------|
|[![Circle CI](https://circleci.com/gh/litehelpers/Cordova-sqlite-storage.svg?style=svg)](https://circleci.com/gh/litehelpers/Cordova-sqlite-storage)|[![Build Status](https://travis-ci.org/litehelpers/Cordova-sqlite-storage.svg?branch=master-rc)](https://travis-ci.org/litehelpers/Cordova-sqlite-storage)|
## IMPORTANT: iCloud backup of SQLite database is NOT allowed
As documented in the "**A Users iCloud Storage Is Limited**" section of [iCloudFundamentals in Mac Developer Library iCloud Design Guide](https://developer.apple.com/library/mac/documentation/General/Conceptual/iCloudDesignGuide/Chapters/iCloudFundametals.html) (near the beginning):
<blockquote>
<ul>
<li><b>DO</b> store the following in iCloud:
<ul>
<li>[<i>other items omitted</i>]</li>
<li>Change log files for a SQLite database (a SQLite databases store file must never be stored in iCloud)</li>
</ul>
</li>
<li><b>DO NOT</b> store the following in iCloud:
<ul>
<li>[<i>items omitted</i>]</li>
</ul>
</li>
</ul>
- <cite><a href="https://developer.apple.com/library/mac/documentation/General/Conceptual/iCloudDesignGuide/Chapters/iCloudFundametals.html">iCloudFundamentals in Mac Developer Library iCloud Design Guide</a>
</blockquote>
### More information about iCloud backup
There are two ways iCloud backup is configured:
- For each app, iCloud backup is configured in `config.xml` **and is enabled by default** (which I think is wrong) as documented at: https://cordova.apache.org/docs/en/5.1.1/guide/platforms/ios/config.html
- In this plugin, the database is stored in the `Documents` subdirectory by default, which is backed up to iCloud. You can use the `location` option in `sqlitePlugin.openDatabase()` to store the database in a subdirectory that is *NOT* backed up to iCloud.
Unless you want your app to use iCloud backup for some reason, it is recommended to turn it off as documented in: https://cordova.apache.org/docs/en/5.1.1/guide/platforms/ios/config.html
I raised [Cordova bug CB-9830](https://issues.apache.org/jira/browse/CB-9830) to disable iCloud backup by default in `config.xml`.
## Available for hire
The primary author and maintainer [@brodybits (Chris Brody)](https://github.com/brodybits) is available for contracting assignments. Part-time contracting assignments would really help keep this project alive. [@brodybits (Chris Brody)](https://github.com/brodybits) can be contacted at: <brodybits@litehelpers.net>
LinkedIn: https://www.linkedin.com/in/chrisbrody
PhoneGap developer page: http://people.phonegap.com/developer/chris-brody
Other projects:
- [liteglue / Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) - Lightweight SQLiteConnection library with abstraction layer over [liteglue / Android-sqlite-native-driver](https://github.com/liteglue/Android-sqlite-native-driver)
- [brodybits / node-uvhttp](https://github.com/brodybits/node-uvhttp) - HTTP server library that serves static content from native code - *under development and currently extremely limited*
- [brodybits / java-node](https://github.com/brodybits/java-node) - two-way binding interface between Java and Node.js (Javascript) - *under development and currently extremely limited*
## Status
- The following features are moved to [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) and removed from this project:
- REGEXP support
- Pre-populated database
- Amazon Fire-OS is dropped due to lack of support by Cordova. Android version should be used to deploy to Fire-OS 5.0(+) devices ref: [cordova/cordova-discuss#32 (comment)](https://github.com/cordova/cordova-discuss/issues/32#issuecomment-167021676)
- Windows "Universal" for Windows 8.0/8.1(+) and Windows Phone 8.1(+) version is in an alpha state with sqlite `3.8.10.2` embedded:
- Issue with UNICODE `\u0000` character (same as `\0`)
- No background processing (for future consideration)
- You *may* encounter issues with Cordova CLI due to [CB-8866](https://issues.apache.org/jira/browse/CB-8866). *Old workaround:* you can install using [litehelpers / cordova-windows-nufix](https://github.com/litehelpers/cordova-windows-nufix) and `plugman` as described below.
- In addition, problems with the Windows "Universal" version have been reported in case of a Cordova project using a Visual Studio template/extension instead of Cordova/PhoneGap CLI or `plugman`
- Not tested with a Windows 10 (or Windows Phone 10) target; Windows 10 build is not expected to work with Windows Phone
- FTS3, FTS4, and R-Tree support is tested working OK in this version (for target platforms Android/iOS/Windows "Universal")
- Status for the other target platforms:
- Android: now using [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) (with sqlite `3.8.10.2` embedded in [Android-sqlite-native-driver](https://github.com/liteglue/Android-sqlite-native-driver)), with support for FTS3/FTS4 and R-Tree
- iOS: sqlite `3.8.10.2` embedded
- WP7: possible to build from C#, as specified by `plugin.xml` - **NOT TESTED**
- WP8: performance/stability issues have been reported with the CSharp-SQLite library. Windows ("Universal") platform is recommended for the future. FTS3/FTS4/R-Tree are NOT supported for WP(7/8).
- Android is supported back to SDK 10 (a.k.a. Gingerbread, Android 2.3.3); support for older versions is available upon request.
- API to open the database may be changed somewhat to be more streamlined. Transaction and single-statement query API will NOT be changed.
- In case of memory issues please use smaller transactions or use the version (with a different licensing scheme) at: [litehelpers / Cordova-sqlite-enterprise-free](https://github.com/litehelpers/Cordova-sqlite-enterprise-free) or [litehelpers / cordova-sqlite-workers-evfree](https://github.com/litehelpers/cordova-sqlite-workers-evfree)
## Announcements
- All iOS operations are now using background processing (reported to resolve intermittent problems with cordova-ios@4.0.1)
- Published [brodybits / Cordova-quick-start-checklist](https://github.com/brodybits/Cordova-quick-start-checklist) and [brodybits / Cordova-troubleshooting-guide](https://github.com/brodybits/Cordova-troubleshooting-guide)
- A version with support for web workers is available (with a different licensing scheme) at: [litehelpers / cordova-sqlite-workers-evfree](https://github.com/litehelpers/cordova-sqlite-workers-evfree)
- A version with pre-populated database support added for Windows "Universal" and REGEXP support added for Android is available at: [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext)
- PhoneGap Build is now supported through the npm package: http://phonegap.com/blog/2015/05/26/npm-plugins-available/
- [MetaMemoryT / websql-promise](https://github.com/MetaMemoryT/websql-promise) now provides a Promises-based interface to both Web SQL and this plugin
- Android version is now using the lightweight [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) by default configuration (may be changed as described below)
- iOS version is now fixed to override the correct pluginInitialize method and should work with recent versions of iOS
- Project has been renamed to prevent confusion with [davibe / Phonegap-SQLitePlugin](https://github.com/davibe/Phonegap-SQLitePlugin) (original version for iOS, with a different API)
- New project location (should redirect)
- The test suite is completely ported to Jasmine (2.2.0) and was used to verify the functionality of the new Windows version
- [SQLCipher](https://www.zetetic.net/sqlcipher/) for Windows (8.1) in addition to Android & iOS is now supported by [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter)
- New `openDatabase` and `deleteDatabase` `location` option to select database location (iOS *only*) and disable iCloud backup
- Fixes to work with PouchDB by [@nolanlawson](https://github.com/nolanlawson)
## Highlights
- Drop-in replacement for HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/), the only change should be `window.openDatabase()` --> `sqlitePlugin.openDatabase()`
- Failure-safe nested transactions with batch processing optimizations
- As described in [this posting](http://brodyspark.blogspot.com/2012/12/cordovaphonegap-sqlite-plugins-offer.html):
- Keeps sqlite database in a user data location that is known; can be reconfigured (iOS version); and synchronized to iCloud by default (iOS version; can be disabled as described below).
- No 5MB maximum, more information at: http://www.sqlite.org/limits.html
## Some apps using this plugin
- [Traiforks Mountain Bike Trail Map App](http://www.trailforks.com/apps/map/) with a couple of nice videos at: <http://www.pinkbike.com/news/trailforks-app-released.html>
- [Get It Done app](http://getitdoneapp.com/) by [marcucio.com](http://marcucio.com/)
- [KAAHE Health Encyclopedia](http://www.kaahe.org/en/index.php?option=com_content&view=article&id=817): Official health app of the Kingdom of Saudi Arabia.
- [Larkwire](http://www.larkwire.com/) (iOS version): Learn bird songs the fun way
- [Tangorin](https://play.google.com/store/apps/details?id=com.tangorin.app) (Android) Japanese Dictionary at [tangorin.com](http://tangorin.com/)
## Known issues
- iOS version does not support certain rapidly repeated open-and-close or open-and-delete test scenarios due to how the implementation handles background processing
- As described below, auto-vacuum is NOT enabled by default.
- INSERT statement that affects multiple rows (due to SELECT cause or using TRIGGER(s), for example) does not report proper rowsAffected on Android in case [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) is disabled (using the `androidDatabaseImplementation` option in `window.sqlitePlugin.openDatabase`)
- Memory issue observed when adding a large number of records due to the JSON implementation which is improved in [litehelpers / Cordova-sqlite-enterprise-free](https://github.com/litehelpers/Cordova-sqlite-enterprise-free) (available with a different licensing scheme)
- A stability issue was reported on the iOS version when in use together with [SockJS](http://sockjs.org/) client such as [pusher-js](https://github.com/pusher/pusher-js) at the same time (see [litehelpers/Cordova-sqlite-storage#196](https://github.com/litehelpers/Cordova-sqlite-storage/issues/196)). The workaround is to call sqlite functions and [SockJS](http://sockjs.org/) client functions in separate ticks (using setTimeout with 0 timeout).
- If a sql statement fails for which there is no error handler or the error handler does not return `false` to signal transaction recovery, the plugin fires the remaining sql callbacks before aborting the transaction.
- In case of an error, the error `code` member is bogus on Android, Windows, and WP(7/8) (fixed for Android in [litehelpers / Cordova-sqlite-enterprise-free](https://github.com/litehelpers/Cordova-sqlite-enterprise-free)).
- Possible crash on Android when using Unicode emoji characters due to [Android bug 81341](https://code.google.com/p/android/issues/detail?id=81341), which _should_ be fixed in Android 6.x
- In-memory database `db=window.sqlitePlugin.openDatabase({name: ":memory:"})` is currently not supported.
- Close database bugs described below.
- When a database is opened and deleted without closing, the iOS version is known to leak resources.
- It is NOT possible to open multiple databases with the same name but in different locations (iOS version).
- DROP table does not actually delete it in WP(7/8) version, due to limitations of CSharp-SQLite.
- On WP(7/8), very large integer values will be stored as negative numbers (see [#195](https://github.com/litehelpers/Cordova-sqlite-storage/issues/195) - fix is available and needs testing)
- Problems reported with PhoneGap Build in the past:
- PhoneGap Build Hydration.
- For some reason, PhoneGap Build may fail to build the iOS version unless the name of the app starts with an uppercase and contains no spaces (see [#243](https://github.com/litehelpers/Cordova-sqlite-storage/issues/243); [Wizcorp/phonegap-facebook-plugin#830](https://github.com/Wizcorp/phonegap-facebook-plugin/issues/830); [phonegap/build#431](https://github.com/phonegap/build/issues/431)).
## Other limitations
- The db version, display name, and size parameter values are not supported and will be ignored.
- This plugin will not work before the callback for the 'deviceready' event has been fired, as described in **Usage**. (This is consistent with the other Cordova plugins.)
- This version will not work within a web worker (not properly supported by the Cordova framework).
- In-memory database `db=window.sqlitePlugin.openDatabase({name: ":memory:"})` is currently not supported.
- The Android version cannot work with more than 100 open db files (due to the threading model used).
- UNICODE line separator (`\u2028`) and paragraph separator (`\u2029`) are currently not supported and known to be broken in iOS version due to [Cordova bug CB-9435](https://issues.apache.org/jira/browse/CB-9435). There *may* be a similar issue with other UNICODE characters in the iOS version (needs further investigation). This is fixed in: [litehelpers / Cordova-sqlite-enterprise-free](https://github.com/litehelpers/Cordova-sqlite-enterprise-free) (available with a different licensing scheme)
- UNICODE characters not working in WP(7/8) version
- Blob type is currently not supported and known to be broken on multiple platforms.
- UNICODE `\u0000` (same as `\0`) character not working in Android (default implementation), Windows "Universal" (8.1/XX) or WP(7/8) versions
- Case-insensitive matching and other string manipulations on Unicode characters, which is provided by optional ICU integration in the sqlite source and working with recent versions of Android, is not supported for any target platforms.
- iOS version uses a thread pool but with only one thread working at a time due to "synchronized" database access
- Large query result can be slow, also due to JSON implementation
- ATTACH another database file is not supported (due to path specifications, which work differently depending on the target platform)
- User-defined savepoints are not supported and not expected to be compatible with the transaction locking mechanism used by this plugin. In addition, the use of BEGIN/COMMIT/ROLLBACK statements is not supported.
- Problems have been reported when using this plugin with Crosswalk (for Android). A couple of things you can try:
- Install Crosswalk as a plugin instead of using Crosswalk to create the project.
- Use `androidDatabaseImplementation: 2` in the openDatabase options as described below.
- Does not work with [axemclion / react-native-cordova-plugin](https://github.com/axemclion/react-native-cordova-plugin) since the `window.sqlitePlugin` object exported (ES5 feature). It is recommended to use [andpor / react-native-sqlite-storage](https://github.com/andpor/react-native-sqlite-storage) for SQLite database access with React Native Android/iOS instead.
## Further testing needed
- Multi-page apps
- Use within [InAppBrowser](http://docs.phonegap.com/en/edge/cordova_inappbrowser_inappbrowser.md.html)
- Use within an iframe (see [litehelpers/Cordova-sqlite-storage#368 (comment)](https://github.com/litehelpers/Cordova-sqlite-storage/issues/368#issuecomment-154046367))
- SAVEPOINT(s)
- On WP(7/8), very large integer values will be stored as negative numbers (see [#195](https://github.com/litehelpers/Cordova-sqlite-storage/issues/195) - fix is available for very large INTEGER values on WP(7/8)
- FTS3/FTS4 is not tested or supported for WP(7/8)
- R-Tree is not tested for Android (in case [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) is disabled), or WP(7/8)
- UNICODE characters not fully tested
- Use with TRIGGER(s), JOIN and ORDER BY RANDOM
- Integration with JXCore for Cordova (must be built without sqlite(3) built-in)
- Delete an open database inside a statement or transaction callback.
## Some tips and tricks
- If you run into problems and your code follows the asynchronous HTML5/[Web SQL](http://www.w3.org/TR/webdatabase/) transaction API, you can try opening your database using `window.openDatabase` and see if you get the same problems.
- In case your database schema may change, it is recommended to keep a table with one row and one column to keep track of your own shema version number. It is possible to add it later. The recommended schema update procedure is described below.
## Common pitfall(s)
- It is NOT allowed to execute sql statements on a transaction following the HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/), as described below.
- It is possible to make a Windows Phone 8.1 project using either the `windows` platform or the `wp8` platform. The `windows` platform is highly recommended over `wp8` whenever possible. Also, some plugins only support `windows` and some plugins support only `wp8`.
- The plugin class name starts with "SQL" in capital letters, but in Javascript the `sqlitePlugin` object name starts with "sql" in small letters.
- Attempting to open a database before receiving the 'deviceready' event callback.
- Inserting STRING into ID field
- Auto-vacuum is NOT enabled by default. It is recommended to periodically VACUUM the database.
## Weird pitfall(s)
- intent whitelist: blocked intent such as external URL intent *may* cause this and perhaps certain Cordova plugin(s) to misbehave (see [litehelpers/Cordova-sqlite-storage#396](https://github.com/litehelpers/Cordova-sqlite-storage/issues/396))
## Angular/ngCordova/Ionic-related pitfalls
- Angular/ngCordova/Ionic controller/factory/service callbacks may be triggered before the 'deviceready' event is fired
- As discussed in [#355](https://github.com/litehelpers/Cordova-sqlite-storage/issues/355), it may be necessary to install ionic-plugin-keyboard
## Major TODOs
- Integrate with IndexedDBShim and some other libraries such as Sequelize, Squel.js, WebSqlSync, Persistence.js, Knex, etc.
## Alternatives
### Other versions
- [litehelpers / Cordova-sqlite-enterprise-free](https://github.com/litehelpers/Cordova-sqlite-enterprise-free) - internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (iOS) - with a different licensing scheme
- [litehelpers / cordova-sqlite-workers-evfree](https://github.com/litehelpers/cordova-sqlite-workers-evfree) - version with support for web workers, includes internal memory improvements to support larger transactions (Android/iOS) and fix to support all Unicode characters (iOS) - with a different licensing scheme
- [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) - version with REGEXP support *added* for Android and pre-populated database support *added* for Windows "Universal" as well
- [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter) - supports [SQLCipher](https://www.zetetic.net/sqlcipher/) for Android, iOS, and Windows (8.1)
- Adaptation for React Native Android and iOS: [andpor / react-native-sqlite-storage](https://github.com/andpor/react-native-sqlite-storage)
- Original version for iOS (with a slightly different transaction API): [davibe / Phonegap-SQLitePlugin](https://github.com/davibe/Phonegap-SQLitePlugin)
### Other SQLite adapter projects
- [object-layer / AnySQL](https://github.com/object-layer/anysql) - Unified SQL API over multiple database engines
- Simpler sqlite plugin with a simpler API: [samikrc / CordovaSQLite](https://github.com/samikrc/CordovaSQLite)
- [an-rahulpandey / cordova-plugin-dbcopy](https://github.com/an-rahulpandey/cordova-plugin-dbcopy) - Alternative way to copy pre-populated database
- [EionRobb / phonegap-win8-sqlite](https://github.com/EionRobb/phonegap-win8-sqlite) - WebSQL add-on for Win8/Metro apps (perhaps with a different API), using an old version of the C++ library from [SQLite3-WinRT Component](https://github.com/doo/SQLite3-WinRT) (as referenced by [01org / cordova-win8](https://github.com/01org/cordova-win8))
- [SQLite3-WinRT Component](https://github.com/doo/SQLite3-WinRT) - C++ component that provides a nice SQLite API with promises for WinJS
- [01org / cordova-win8](https://github.com/01org/cordova-win8) - old, unofficial version of Cordova API support for Windows 8 Metro that includes an old version of the C++ [SQLite3-WinRT Component](https://github.com/doo/SQLite3-WinRT)
- [MSOpenTech / cordova-plugin-websql](https://github.com/MSOpenTech/cordova-plugin-websql) - Windows 8(+) and Windows Phone 8(+) WebSQL plugin versions in C#
- [Thinkwise / cordova-plugin-websql](https://github.com/Thinkwise/cordova-plugin-websql) - fork of [MSOpenTech / cordova-plugin-websql](https://github.com/MSOpenTech/cordova-plugin-websql) that supports asynchronous execution
- [MetaMemoryT / websql-client](https://github.com/MetaMemoryT/websql-client) - provides the same API and connects to [websql-server](https://github.com/MetaMemoryT/websql-server) through WebSockets.
### Alternative solutions
- [ABB-Austin / cordova-plugin-indexeddb-async](https://github.com/ABB-Austin/cordova-plugin-indexeddb-async) - Asynchronous IndexedDB plugin for Cordova that uses [axemclion / IndexedDBShim](https://github.com/axemclion/IndexedDBShim) (Browser/iOS/Android/Windows) and [Thinkwise / cordova-plugin-websql](https://github.com/Thinkwise/cordova-plugin-websql) - (Windows)
- Another sqlite binding for React-Native (iOS version): [almost/react-native-sqlite](https://github.com/almost/react-native-sqlite)
- Use [NativeScript](https://www.nativescript.org) with its web view and [NathanaelA / nativescript-sqlite](https://github.com/Natha
naelA/nativescript-sqlite) (Android and/or iOS)
- Standard HTML5 [local storage](https://en.wikipedia.org/wiki/Web_storage#localStorage)
- [Realm.io](https://realm.io/)
# Usage
The idea is to emulate the HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/) as closely as possible. The only major change is to use `window.sqlitePlugin.openDatabase()` (or `sqlitePlugin.openDatabase()`) instead of `window.openDatabase()`. If you see any other major change please report it, it is probably a bug.
**NOTE:** If a sqlite statement in a transaction fails with an error, the error handler *must* return `false` in order to recover the transaction. This is correct according to the HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/) standard. This is different from the WebKit implementation of Web SQL in Android and iOS which recovers the transaction if a sql error hander returns a non-`true` value.
## Opening a database
There are two options to open a database access object:
- **Recommended:** `var db = window.sqlitePlugin.openDatabase({name: "my.db", location: 1}, successcb, errorcb);`
- **Classical:** `var db = window.sqlitePlugin.openDatabase("myDatabase.db", "1.0", "Demo", -1);`
The `location` option is used to select the database subdirectory location (iOS *only*) with the following choices:
- `0` (default): `Documents` - visible to iTunes and backed up by iCloud
- `1`: `Library` - backed up by iCloud, *NOT* visible to iTunes
- `2`: `Library/LocalDatabase` - *NOT* visible to iTunes and *NOT* backed up by iCloud
**IMPORTANT:** Please wait for the 'deviceready' event, as in the following example:
```js
// Wait for Cordova to load
document.addEventListener('deviceready', onDeviceReady, false);
// Cordova is ready
function onDeviceReady() {
var db = window.sqlitePlugin.openDatabase({name: "my.db"});
// ...
}
```
The successcb and errorcb callback parameters are optional but can be extremely helpful in case anything goes wrong. For example:
```js
window.sqlitePlugin.openDatabase({name: "my.db"}, function(db) {
db.transaction(function(tx) {
// ...
}, function(err) {
console.log('Open database ERROR: ' + JSON.stringify(err));
});
});
```
If any sql statements or transactions are attempted on a database object before the openDatabase result is known, they will be queued and will be aborted in case the database cannot be opened.
**OTHER NOTES:**
- The database file name should include the extension, if desired.
- It is possible to open multiple database access objects for the same database.
- The database access object can be closed as described below.
### Android sqlite implementation
By default, this plugin uses [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector), which is lightweight and should be more efficient than the built-in Android database classes. To use the built-in Android database classes instead:
```js
var db = window.sqlitePlugin.openDatabase({name: "my.db", androidDatabaseImplementation: 2});
```
### Workaround for Android db locking issue
[Issue #193](https://github.com/litehelpers/Cordova-sqlite-storage/issues/193) was reported (as observed by several app developers) that on some newer versions of the Android database classes, if the app is stopped or aborted without closing the database then:
- (sometimes) there is an unexpected database lock
- the data that was inserted is lost.
This issue is suspected to be caused by [this Android sqlite commit](https://github.com/android/platform_external_sqlite/commit/d4f30d0d1544f8967ee5763c4a1680cb0553039f), which references and includes the sqlite commit at: http://www.sqlite.org/src/info/6c4c2b7dba
There is an optional workaround that simply closes and reopens the database file at the end of every transaction that is committed. The workaround is enabled by opening the database with options as follows:
```js
var db = window.sqlitePlugin.openDatabase({name: "my.db", androidDatabaseImplementation: 2, androidLockWorkaround: 1});
```
This option is ignored if `androidDatabaseImplementation: 2` is not specified.
**IMPORTANT NOTE:** This workaround is *only* applied when using `db.transaction()`, *not* applied when running `executeSql()` on the database object.
## SQL transactions
The following types of SQL transactions are supported by this version:
- Single-statement transactions
- Standard asynchronous transactions
### Single-statement transactions
Sample:
```Javascript
db.executeSql("SELECT LENGTH('tenletters') AS stringlength", [], function (res) {
console.log('got stringlength: ' + res.rows.item(0).stringlength);
}, function(error) {
console.log('SELECT error: ' + error.message);
});
```
## Standard asynchronous transactions
Standard asynchronous transactions follow the HTML5/[Web SQL API](http://www.w3.org/TR/webdatabase/) which is very well documented and uses BEGIN and COMMIT or ROLLBACK to keep the transactions failure-safe. Here is a very simple example from the test suite:
```Javascript
db.transaction(function(tx) {
tx.executeSql("SELECT UPPER('Some US-ASCII text') AS uppertext", [], function(tx, res) {
console.log("res.rows.item(0).uppertext: " + res.rows.item(0).uppertext);
}, function(error) {
console.log('SELECT error: ' + error.message);
});
}, function(error) {
console.log('transaction error: ' + error.message);
}, function() {
console.log('transaction ok');
});
```
In case of a read-only transaction, it is possible to use `readTransaction` which will not use BEGIN, COMMIT, or ROLLBACK:
```Javascript
db.readTransaction(function(tx) {
tx.executeSql("SELECT UPPER('Some US-ASCII text') AS uppertext", [], function(tx, res) {
console.log("res.rows.item(0).uppertext: " + res.rows.item(0).uppertext);
}, function(error) {
console.log('SELECT error: ' + error.message);
});
}, function(error) {
console.log('transaction error: ' + error.message);
}, function() {
console.log('transaction ok');
});
```
**WARNING:** It is NOT allowed to execute sql statements on a transaction after it has finished. Here is an example from my [Populating Cordova SQLite storage with the JQuery API post](http://www.brodybits.com/cordova/sqlite/api/jquery/2015/10/26/populating-cordova-sqlite-storage-with-the-jquery-api.html):
```Javascript
// BROKEN SAMPLE:
var db = window.sqlitePlugin.openDatabase({name: "test.db"});
db.executeSql("DROP TABLE IF EXISTS tt");
db.executeSql("CREATE TABLE tt (data)");
db.transaction(function(tx) {
$.ajax({
url: 'https://api.github.com/users/litehelpers/repos',
dataType: 'json',
success: function(res) {
console.log('Got AJAX response: ' + JSON.stringify(res));
$.each(res, function(i, item) {
console.log('REPO NAME: ' + item.name);
tx.executeSql("INSERT INTO tt values (?)", JSON.stringify(item.name));
});
}
});
}, function(e) {
console.log('Transaction error: ' + e.message);
}, function() {
// Check results:
db.executeSql('SELECT COUNT(*) FROM tt', [], function(res) {
console.log('Check SELECT result: ' + JSON.stringify(res.rows.item(0)));
});
});
```
You can find more details and a step-by-step description how to do this right in the [Populating Cordova SQLite storage with the JQuery API post](http://www.brodybits.com/cordova/sqlite/api/jquery/2015/10/26/populating-cordova-sqlite-storage-with-the-jquery-api.html):
## Background processing
The threading model depends on which version is used:
- For Android and WP(7/8), one background thread per db;
- for iOS, background processing using a very limited thread pool (only one thread working at a time);
- for Windows "Universal" (8.1), no background processing (for future consideration).
# Sample with PRAGMA feature
This is a pretty strong test: first we create a table and add a single entry, then query the count to check if the item was inserted as expected. Note that a new transaction is created in the middle of the first callback.
```js
// Wait for Cordova to load
document.addEventListener('deviceready', onDeviceReady, false);
// Cordova is ready
function onDeviceReady() {
var db = window.sqlitePlugin.openDatabase({name: "my.db"});
db.transaction(function(tx) {
tx.executeSql('DROP TABLE IF EXISTS test_table');
tx.executeSql('CREATE TABLE IF NOT EXISTS test_table (id integer primary key, data text, data_num integer)');
// demonstrate PRAGMA:
db.executeSql("pragma table_info (test_table);", [], function(res) {
console.log("PRAGMA res: " + JSON.stringify(res));
});
tx.executeSql("INSERT INTO test_table (data, data_num) VALUES (?,?)", ["test", 100], function(tx, res) {
console.log("insertId: " + res.insertId + " -- probably 1");
console.log("rowsAffected: " + res.rowsAffected + " -- should be 1");
db.transaction(function(tx) {
tx.executeSql("select count(id) as cnt from test_table;", [], function(tx, res) {
console.log("res.rows.length: " + res.rows.length + " -- should be 1");
console.log("res.rows.item(0).cnt: " + res.rows.item(0).cnt + " -- should be 1");
});
});
}, function(e) {
console.log("ERROR: " + e.message);
});
});
}
```
**NOTE:** PRAGMA statements must be executed in `executeSql()` on the database object (i.e. `db.executeSql()`) and NOT within a transaction.
## Sample with transaction-level nesting
In this case, the same transaction in the first executeSql() callback is being reused to run executeSql() again.
```js
// Wait for Cordova to load
document.addEventListener('deviceready', onDeviceReady, false);
// Cordova is ready
function onDeviceReady() {
var db = window.sqlitePlugin.openDatabase("Database", "1.0", "Demo", -1);
db.transaction(function(tx) {
tx.executeSql('DROP TABLE IF EXISTS test_table');
tx.executeSql('CREATE TABLE IF NOT EXISTS test_table (id integer primary key, data text, data_num integer)');
tx.executeSql("INSERT INTO test_table (data, data_num) VALUES (?,?)", ["test", 100], function(tx, res) {
console.log("insertId: " + res.insertId + " -- probably 1");
console.log("rowsAffected: " + res.rowsAffected + " -- should be 1");
tx.executeSql("select count(id) as cnt from test_table;", [], function(tx, res) {
console.log("res.rows.length: " + res.rows.length + " -- should be 1");
console.log("res.rows.item(0).cnt: " + res.rows.item(0).cnt + " -- should be 1");
});
}, function(e) {
console.log("ERROR: " + e.message);
});
});
}
```
This case will also works with Safari (WebKit), assuming you replace `window.sqlitePlugin.openDatabase` with `window.openDatabase`.
## Close a database object
```js
db.close(successcb, errorcb);
```
It is OK to close the database within a transaction callback but *NOT* within a statement callback. The following example is OK:
```Javascript
db.transaction(function(tx) {
tx.executeSql("SELECT LENGTH('tenletters') AS stringlength", [], function(tx, res) {
console.log('got stringlength: ' + res.rows.item(0).stringlength);
});
}, function(error) {
// OK to close here:
console.log('transaction error: ' + error.message);
db.close();
}, function() {
// OK to close here:
console.log('transaction ok');
db.close(function() {
console.log('database is closed ok');
});
});
```
The following example is NOT OK:
```Javascript
// BROKEN:
db.transaction(function(tx) {
tx.executeSql("SELECT LENGTH('tenletters') AS stringlength", [], function(tx, res) {
console.log('got stringlength: ' + res.rows.item(0).stringlength);
// BROKEN - this will trigger the error callback:
db.close(function() {
console.log('database is closed ok');
}, function(error) {
console.log('ERROR closing database');
});
});
});
```
**BUG 1:** It is currently NOT possible to close a database in a `db.executeSql` callback. For example:
```Javascript
// BROKEN DUE TO BUG:
db.executeSql("SELECT LENGTH('tenletters') AS stringlength", [], function (res) {
var stringlength = res.rows.item(0).stringlength;
console.log('got stringlength: ' + res.rows.item(0).stringlength);
// BROKEN - this will trigger the error callback DUE TO BUG:
db.close(function() {
console.log('database is closed ok');
}, function(error) {
console.log('ERROR closing database');
});
});
```
**BUG 2:** If multiple database access objects are opened for the same database and one database access object is closed, the database is no longer available for the other database access objects. Possible workarounds:
- It is still possible to open one or more new database access objects on a database that has been closed.
- It *should* be OK not to explicitly close a database handle since database transactions are [ACID](https://en.wikipedia.org/wiki/ACID) compliant and the app's memory resources are cleaned up by the system upon termination.
## Delete a database
```js
window.sqlitePlugin.deleteDatabase({name: "my.db", location: 1}, successcb, errorcb);
```
`location` as described above for `openDatabase` (iOS *only*)
**NOTE:** not implemented for Windows "Universal" (8.1) version.
# Schema versions
The transactional nature of the API makes it relatively straightforward to manage a database schema that may be upgraded over time (adding new columns or new tables, for example). Here is the recommended procedure to follow upon app startup:
- Check your database schema version number (you can use `db.executeSql` since it should be a very simple query)
- If your database needs to be upgraded, do the following *within a single transaction* to be failure-safe:
- Create your database schema version table (single row single column) if it does not exist (you can check the `sqlite_master` table as described at: http://stackoverflow.com/questions/1601151/how-do-i-check-in-sqlite-whether-a-table-exists)
- Add any missing columns and tables, and apply any other changes necessary
**IMPORTANT:** Since we cannot be certain when the users will actually update their apps, old schema versions will have to be supported for a very long time.
## Use with Ionic/ngCordova/Angular
It is recommended to follow the tutorial at: https://blog.nraboy.com/2014/11/use-sqlite-instead-local-storage-ionic-framework/
Documentation at: http://ngcordova.com/docs/plugins/sqlite/
# Installing
## Windows "Universal" target platform
**IMPORTANT:** There are issues supporing certain Windows target platforms due to [CB-8866](https://issues.apache.org/jira/browse/CB-8866):
- When using Visual Studio, the default target ("Mixed Platforms") will not work
- Problems have been with the Windows "Universal" version case of a Cordova project using a Visual Studio template/extension instead of Cordova/PhoneGap CLI or `plugman`
*Old workaround:* As an alternative, which will support the ("Mixed Platforms") target, you can use `plugman` instead with [litehelpers / cordova-windows-nufix](https://github.com/litehelpers/cordova-windows-nufix), as described here.
### Old workaround - Using plugman to support "Mixed Platforms"
- make sure you have the latest version of `plugman` installed: `npm install -g plugman`
- Download the [cordova-windows-nufix 3.9.0-nufixpre-01 zipball](https://github.com/litehelpers/cordova-windows-nufix/archive/3.9.0-nufixpre-01.zip) (or you can clone [litehelpers / cordova-windows-nufix](https://github.com/litehelpers/cordova-windows-nufix) instead)
- Create your Windows "Universal" (8.1) project using [litehelpers / cordova-windows-nufix](https://github.com/litehelpers/cordova-windows-nufix):
- `path.to.cordova-windows-nufix/bin/create.bat your_app_path your.app.id YourAppName`
- `cd your_app_path` and install plugin using `plugman`:
- `plugman install --platform windows --project . --plugin cordova-sqlite-storage`
- Put your sql program in your project `www` (don't forget to reference it from `www\index.html` and wait for `deviceready` event)
Then your project in `CordovaApp.sln` should work with "Mixed Platforms" on both Windows 8.1 and Windows Phone 8.1.
## Easy install with Cordova CLI tool
npm install -g cordova # if you don't have cordova
cordova create MyProjectFolder com.my.project MyProject && cd MyProjectFolder # if you are just starting
cordova plugin add cordova-sqlite-storage
You can find more details at [this writeup](http://iphonedevlog.wordpress.com/2014/04/07/installing-chris-brodys-sqlite-database-with-cordova-cli-android/).
**WARNING:** as stated above, there are issues using Cordova CLI with Windows ("Universal") target platform due to [CB-8866](https://issues.apache.org/jira/browse/CB-8866). It is recommended to use `plugman` instead, as described above.
**IMPORTANT:** sometimes you have to update the version for a platform before you can build, like: `cordova prepare ios`
**NOTE:** If you cannot build for a platform after `cordova prepare`, you may have to remove the platform and add it again, such as:
cordova platform rm ios
cordova platform add ios
**EXTRA NOTE:** You can use https://github.com/litehelpers/Cordova-sqlite-storage instead of `cordova-sqlite-storage` to get the latest version directly from github.
## Easy install with plugman tool
```shell
plugman install --platform MYPLATFORM --project path.to.my.project.folder --plugin cordova-sqlite-storage
```
where MYPLATFORM is `android`, `ios`, `windows`, or `wp8`.
A posting how to get started developing on Windows host without the Cordova CLI tool (for Android target only) is available [here](http://brodybits.blogspot.com/2015/03/trying-cordova-for-android-on-windows-without-cordova-cli.html).
**EXTRA NOTE:** You can use https://github.com/litehelpers/Cordova-sqlite-storage instead of `cordova-sqlite-storage` to get the latest version directly from github.
## Source tree
- `SQLitePlugin.coffee.md`: platform-independent (Literate coffee-script, can be read by recent coffee-script compiler)
- `www`: `SQLitePlugin.js` platform-independent Javascript as generated from `SQLitePlugin.coffee.md` (and checked in!)
- `src`: platform-specific source code:
- `external` - placeholder for external dependencies - *not required in this version*
- `android` - Java plugin code for Android (along with [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector) and [Android-sqlite-native-driver](https://github.com/liteglue/Android-sqlite-native-driver) library jars)
- `ios` - Objective-C plugin code for iOS;
- `windows` - Javascript proxy code and SQLite3-WinRT project for Windows "Universal" (8.1);
- `wp` - C-sharp code for WP(7/8)
- `spec`: test suite using Jasmine (2.2.0), ported from QUnit `test-www` test suite, working on all platforms
- `tests`: very simple Jasmine test suite that is run on Circle CI (Android version) and Travis CI (iOS version)
- `Lawnchair-adapter`: Lawnchair adaptor, based on the version from the Lawnchair repository, with the basic Lawnchair test suite in `test-www` subdirectory
## Manual installation - Android version
These installation instructions are based on the Android example project from Cordova/PhoneGap 2.7.0, using the `lib/android/example` subdirectory from the PhoneGap 2.7 zipball.
- Install `SQLitePlugin.js` from `www` into `assets/www`
- Install `SQLitePlugin.java` from `src/android/io/liteglue` into `src/io/liteglue` subdirectory
- Install the `libs` subtree with 2 jars from `src/android/libs` into your Android project
- Add the plugin element `<plugin name="SQLitePlugin" value="io.liteglue.SQLitePlugin"/>` to `res/xml/config.xml`
Sample change to `res/xml/config.xml` for Cordova/PhoneGap 2.x:
```diff
--- config.xml.orig 2015-04-14 14:03:05.000000000 +0200
+++ res/xml/config.xml 2015-04-14 14:08:08.000000000 +0200
@@ -36,6 +36,7 @@
<preference name="useBrowserHistory" value="true" />
<preference name="exit-on-suspend" value="false" />
<plugins>
+ <plugin name="SQLitePlugin" value="io.liteglue.SQLitePlugin"/>
<plugin name="App" value="org.apache.cordova.App"/>
<plugin name="Geolocation" value="org.apache.cordova.GeoBroker"/>
<plugin name="Device" value="org.apache.cordova.Device"/>
```
Before building for the first time, you have to update the project with the desired version of the Android SDK with a command like:
android update project --path $(pwd) --target android-19
(assuming Android SDK 19, use the correct desired Android SDK number here)
**NOTE:** using this plugin on Cordova pre-3.0 requires the following changes to `SQLiteAndroidDatabase.java` and `SQLitePlugin.java`:
```diff
--- Cordova-sqlite-storage/src/android/io/liteglue/SQLiteAndroidDatabase.java 2015-04-14 14:05:01.000000000 +0200
+++ src/io/liteglue/SQLiteAndroidDatabase.java 2015-04-14 14:15:23.000000000 +0200
@@ -24,7 +24,7 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import org.apache.cordova.CallbackContext;
+import org.apache.cordova.api.CallbackContext;
import org.json.JSONArray;
import org.json.JSONException;
diff -u Cordova-sqlite-storage/src/android/io/liteglue/SQLitePlugin.java src/io/liteglue/SQLitePlugin.java
--- Cordova-sqlite-storage/src/android/io/liteglue/SQLitePlugin.java 2015-04-14 14:05:01.000000000 +0200
+++ src/io/liteglue/SQLitePlugin.java 2015-04-14 14:10:44.000000000 +0200
@@ -22,8 +22,8 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import org.apache.cordova.CallbackContext;
-import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.api.CallbackContext;
+import org.apache.cordova.api.CordovaPlugin;
import org.json.JSONArray;
import org.json.JSONException;
```
## Manual installation - iOS version
### SQLite library
In the Project "Build Phases" tab, select the _first_ "Link Binary with Libraries" dropdown menu and add the library `libsqlite3.dylib` or `libsqlite3.0.dylib`.
**NOTE:** In the "Build Phases" there can be multiple "Link Binary with Libraries" dropdown menus. Please select the first one otherwise it will not work.
### SQLite Plugin
- Copy `SQLitePlugin.[hm]` from `src/ios` into your project Plugins folder and add them in XCode (I always just have "Create references" as the option selected).
- Copy `SQLitePlugin.js` from `www` into your project `www` folder
- Enable the SQLitePlugin in `config.xml`
Sample change to `config.xml` for Cordova/PhoneGap 2.x:
```diff
--- config.xml.old 2013-05-17 13:18:39.000000000 +0200
+++ config.xml 2013-05-17 13:18:49.000000000 +0200
@@ -39,6 +39,7 @@
<content src="index.html" />
<plugins>
+ <plugin name="SQLitePlugin" value="SQLitePlugin" />
<plugin name="Device" value="CDVDevice" />
<plugin name="Logger" value="CDVLogger" />
<plugin name="Compass" value="CDVLocation" />
```
## Manual installation - Windows "Universal" (8.1) version
Described above.
## Manual installation - WP(7/8) version
TBD
## Quick installation test
Assuming your app has a recent template as used by the Cordova create script, add the following code to the `onDeviceReady` function, after `app.receivedEvent('deviceready');`:
```Javascript
window.sqlitePlugin.openDatabase({ name: 'hello-world.db' }, function (db) {
db.executeSql("select length('tenletters') as stringlength", [], function (res) {
var stringlength = res.rows.item(0).stringlength;
console.log('got stringlength: ' + stringlength);
document.getElementById('deviceready').querySelector('.received').innerHTML = 'stringlength: ' + stringlength;
});
});
```
### Old installation test
Make a change like this to index.html (or use the sample code) verify proper installation:
```diff
--- index.html.old 2012-08-04 14:40:07.000000000 +0200
+++ assets/www/index.html 2012-08-04 14:36:05.000000000 +0200
@@ -24,7 +24,35 @@
<title>PhoneGap</title>
<link rel="stylesheet" href="master.css" type="text/css" media="screen" title="no title">
<script type="text/javascript" charset="utf-8" src="cordova-2.0.0.js"></script>
- <script type="text/javascript" charset="utf-8" src="main.js"></script>
+ <script type="text/javascript" charset="utf-8" src="SQLitePlugin.js"></script>
+
+
+ <script type="text/javascript" charset="utf-8">
+ document.addEventListener("deviceready", onDeviceReady, false);
+ function onDeviceReady() {
+ var db = window.sqlitePlugin.openDatabase("Database", "1.0", "Demo", -1);
+
+ db.transaction(function(tx) {
+ tx.executeSql('DROP TABLE IF EXISTS test_table');
+ tx.executeSql('CREATE TABLE IF NOT EXISTS test_table (id integer primary key, data text, data_num integer)');
+
+ tx.executeSql("INSERT INTO test_table (data, data_num) VALUES (?,?)", ["test", 100], function(tx, res) {
+ console.log("insertId: " + res.insertId + " -- probably 1"); // check #18/#38 is fixed
+ alert("insertId: " + res.insertId + " -- should be valid");
+
+ db.transaction(function(tx) {
+ tx.executeSql("SELECT data_num from test_table;", [], function(tx, res) {
+ console.log("res.rows.length: " + res.rows.length + " -- should be 1");
+ alert("res.rows.item(0).data_num: " + res.rows.item(0).data_num + " -- should be 100");
+ });
+ });
+
+ }, function(e) {
+ console.log("ERROR: " + e.message);
+ });
+ });
+ }
+ </script>
</head>
<body onload="init();" id="stage" class="theme">
```
# Support
## Policy
As described above, free support will be provided on a very limited basis due to some changing priorities. In addition, free support is only available in public forums. Please follow the steps below to be sure you have done your best before requesting help.
Commercial support is available by contacting: <info@litehelpers.net>
## Before asking for help
First steps:
- Verify that you have followed the steps in [brodybits / Cordova-quick-start-checklist](https://github.com/brodybits/Cordova-quick-start-checklist)
- Check the troubleshooting steps and pitfalls in [brodybits / Cordova-troubleshooting-guide](https://github.com/brodybits/Cordova-troubleshooting-guide)
and check the following:
- You are using the latest version of the Plugin Javascript & platform-specific Java or Objective-C source from this repository.
- You have installed the Javascript & platform-specific Java or Objective-C correctly.
- You have included the correct version of the cordova Javascript and SQLitePlugin.js and got the path right.
- You have registered the plugin properly in `config.xml`.
If you still cannot get something to work:
- create a fresh, clean Cordova project;
- add this plugin according to the instructions above;
- double-check that you follwed the steps in [brodybits / Cordova-quick-start-checklist](https://github.com/brodybits/Cordova-quick-start-checklist);
- try a simple test program;
- double-check the troubleshooting steps and pitfalls in [brodybits / Cordova-troubleshooting-guide](https://github.com/brodybits/Cordova-troubleshooting-guide)
If you continue to see the issue in the fresh, clean Cordova project:
- Make the simplest test program you can to demonstrate the issue, including the following characteristics:
- it completely self-contained, i.e. it is using no extra libraries beyond cordova & SQLitePlugin.js;
- if the issue is with *adding* data to a table, that the test program includes the statements you used to open the database and create the table;
- if the issue is with *retrieving* data from a table, that the test program includes the statements you used to open the database, create the table, and enter the data you are trying to retrieve.
## What will be supported for free
Please make a small, self-contained test program that can demonstrate your problem and post it. Please do not use any other plugins or frameworks than are absolutely necessary to demonstrate your problem.
In case of a problem with a pre-populated database, please post your entire project.
## Support for issues with Angular/"ngCordova"/Ionic
Free support for issues with Angular/"ngCordova"/Ionic will only be provided if you can demonstrate that you can do the same thing without such a framework.
- Make a fresh, clean ngCordova or Ionic project with a test program that demonstrates the issue and post it. Please do not use any other plugins or frameworks unless absolutely necessary to demonstrate your issue.
- Make another project without any form of Angular including ngCordova or Ionic, with the same test program to show that it will work outside Angular/"ngCordova"/Ionic.
## What information is needed for help
Please include the following:
- Which platform(s) Android/iOS/WP8/Windows 8.1/Windows Phone 8.1
- Clear description of the issue
- A small, complete, self-contained program that demonstrates the problem, preferably as a Github project. ZIP/TGZ/BZ2 archive available from a public link is OK. No RAR or other such formats please!
- A Cordova project is highly preferred. Intel, MS IDE, or similar project formats should be avoided.
## Please do NOT use any of these formats
- screen casts or videos
- RAR or similar archive formats
- Intel, MS IDE, or similar project formats unless absolutely necessary
## Where to ask for help
Once you have followed the directions above, you may request free support in the following location(s):
- [litehelpers / Cordova-sqlite-storage / issues](https://github.com/litehelpers/Cordova-sqlite-storage/issues)
- TBD ???: ~~[litehelpers / Cordova-sqlite-help](https://github.com/litehelpers/Cordova-sqlite-help)~~
Please include the information described above otherwise.
## Professional support
Professional support is available, please contact: <info@litehelpers.net>
# Unit tests
Unit testing is done in `spec`.
## running tests from shell
To run the tests from \*nix shell, simply do either:
./bin/test.sh ios
or for Android:
./bin/test.sh android
To run then from a windows powershell do either
.\bin\test.ps1 android
or for Windows (8.1):
.\bin\test.ps1 windows
or for Windows Phone (7/8):
.\bin\test.ps1 wp8
# Adapters
## Lawnchair Adapter
### Common adapter
Please look at the `Lawnchair-adapter` tree that contains a common adapter, which should also work with the Android version, along with a test-www directory.
### Included files
Include the following Javascript files in your HTML:
- `cordova.js` (don't forget!)
- `lawnchair.js` (you provide)
- `SQLitePlugin.js` (in case of Cordova pre-3.0)
- `Lawnchair-sqlitePlugin.js` (must come after `SQLitePlugin.js` in case of Cordova pre-3.0)
### Sample
The `name` option determines the sqlite database filename, *with no extension automatically added*. Optionally, you can change the db filename using the `db` option.
In this example, you would be using/creating a database with filename `kvstore`:
```Javascript
kvstore = new Lawnchair({name: "kvstore"}, function() {
// do stuff
);
```
Using the `db` option you can specify the filename with the desired extension and be able to create multiple stores in the same database file. (There will be one table per store.)
```Javascript
recipes = new Lawnchair({db: "cookbook", name: "recipes", ...}, myCallback());
ingredients = new Lawnchair({db: "cookbook", name: "ingredients", ...}, myCallback());
```
**KNOWN ISSUE:** the new db options are *not* supported by the Lawnchair adapter. The workaround is to first open the database file using `sqlitePlugin.openDatabase()`.
## PouchDB
The adapter is now part of [PouchDB](http://pouchdb.com/) thanks to [@nolanlawson](https://github.com/nolanlawson), see [PouchDB FAQ](http://pouchdb.com/faq.html).
**NOTE:** The PouchDB adapter has not been tested with the new [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector). You may need to include `androidDatabaseImplementation: 2` in the `sqlitePlugin.openDatabase()` options as described above.
# Contributing
## Community
- Testimonials of apps that are using this plugin would be especially helpful.
- Reporting issues at [litehelpers / Cordova-sqlite-storage / issues](https://github.com/litehelpers/Cordova-sqlite-storage/issues) can help improve the quality of this plugin.
## Code
**WARNING:** Please do NOT propose changes from your default branch. In general, contributions are rebased using `git rebase` or `git cherry-pick` and not merged.
- Patches with bug fixes are helpful, especially when submitted with test code.
- Other enhancements welcome for consideration, when submitted with test code and are working for all supported platforms. Increase of complexity should be avoided.
- All contributions may be reused by [@brodybits (Chris Brody)](https://github.com/brodybits) under another license in the future. Efforts will be taken to give credit for major contributions but it will not be guaranteed.
- Project restructuring, i.e. moving files and/or directories around, should be avoided if possible.
- If you see a need for restructuring, it is better to discuss it first in a [new issue](https://github.com/litehelpers/Cordova-sqlite-storage/issues/new) where alternatives can be discussed before reaching a conclusion. If you want to propose a change to the project structure:
- Remember to make (and use) a special branch within your fork from which you can send the proposed restructuring;
- Always use `git mv` to move files & directories;
- Never mix a move/rename operation with any other changes in the same commit.
## Other
[@brodybits (Chris Brody)](https://github.com/brodybits) and others contribute their valuable time and expertise to maintain this project for the benefit of the mobile app community. Small consulting relationships can help strengthen the business viability of this project (see contact below).
## Major branches
- `common-src` - source for Android, iOS, and Windows (8.1) versions (shared with [litehelpers / Cordova-sqlcipher-adapter](https://github.com/litehelpers/Cordova-sqlcipher-adapter))
- `new-common-rc` - pre-release version for Android/iOS/Windows (8.1), including library dependencies for Android and Windows (8.1)
- `wp-src` - source for Android (*not* using [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector)), iOS, and WP(7/8) versions
- `wp-master-rc` - pre-release version for Android(*not* using [Android-sqlite-connector](https://github.com/liteglue/Android-sqlite-connector))/iOS/WP(7/8), including source for CSharp-SQLite (C#) library classes
- `master-public` - release version for all supported platforms, including library dependencies for Android, Windows (8.1), and WP(7/8)
- [FUTURE TBD] ~~`master` - version for release, to be included in PhoneGap build.~~
## Contact
<info@litehelpers.net>

View File

@ -0,0 +1,602 @@
# SQLite plugin in Markdown (litcoffee)
#### Use coffee compiler to compile this directly into Javascript
#### License for common script: MIT or Apache
# Top-level SQLite plugin objects
## root window object:
root = @
## constant(s):
READ_ONLY_REGEX = /^\s*(?:drop|delete|insert|update|create)\s/i
# per-db state
DB_STATE_INIT = "INIT"
DB_STATE_OPEN = "OPEN"
## global(s):
# per-db map of locking and queueing
# XXX NOTE: This is NOT cleaned up when a db is closed and/or deleted.
# If the record is simply removed when a db is closed or deleted,
# it will cause some test failures and may break large-scale
# applications that repeatedly open and close the database.
# [BUG #210] TODO: better to abort and clean up the pending transaction state.
# XXX TBD this will be renamed and include some more per-db state.
txLocks = {}
## utility functions:
# Errors returned to callbacks must conform to `SqlError` with a code and message.
# Some errors are of type `Error` or `string` and must be converted.
newSQLError = (error, code) ->
sqlError = error
code = 0 if !code # unknown by default
if !sqlError
sqlError = new Error "a plugin had an error but provided no response"
sqlError.code = code
if typeof sqlError is "string"
sqlError = new Error error
sqlError.code = code
if !sqlError.code && sqlError.message
sqlError.code = code
if !sqlError.code && !sqlError.message
sqlError = new Error "an unknown error was returned: " + JSON.stringify(sqlError)
sqlError.code = code
return sqlError
nextTick = window.setImmediate || (fun) ->
window.setTimeout(fun, 0)
return
###
Utility that avoids leaking the arguments object. See
https://www.npmjs.org/package/argsarray
###
argsArray = (fun) ->
return ->
len = arguments.length
if len
args = []
i = -1
while ++i < len
args[i] = arguments[i]
return fun.call this, args
else
return fun.call this, []
## SQLite plugin db-connection handle
#### NOTE: there can be multipe SQLitePlugin db-connection handles per open db.
#### SQLite plugin db connection handle object is defined by a constructor function and prototype member functions:
SQLitePlugin = (openargs, openSuccess, openError) ->
# console.log "SQLitePlugin openargs: #{JSON.stringify openargs}"
if !(openargs and openargs['name'])
throw newSQLError "Cannot create a SQLitePlugin db instance without a db name"
dbname = openargs.name
if typeof dbname != 'string'
throw newSQLError 'sqlite plugin database name must be a string'
@openargs = openargs
@dbname = dbname
@openSuccess = openSuccess
@openError = openError
@openSuccess or
@openSuccess = ->
console.log "DB opened: " + dbname
return
@openError or
@openError = (e) ->
console.log e.message
return
@open @openSuccess, @openError
return
SQLitePlugin::databaseFeatures = isSQLitePluginDatabase: true
# Keep track of state of open db connections
# XXX TBD this will be moved and renamed or
# combined with txLocks.
SQLitePlugin::openDBs = {}
SQLitePlugin::addTransaction = (t) ->
if !txLocks[@dbname]
txLocks[@dbname] = {
queue: []
inProgress: false
}
txLocks[@dbname].queue.push t
if @dbname of @openDBs && @openDBs[@dbname] isnt DB_STATE_INIT
# XXX TODO: only when queue has length of 1 [and test it!!]
@startNextTransaction()
else
if @dbname of @openDBs
console.log 'new transaction is waiting for open operation'
else
# XXX TBD TODO: in this case (which should not happen), should abort and discard the transaction.
console.log 'database is closed, new transaction is [stuck] waiting until db is opened again!'
return
SQLitePlugin::transaction = (fn, error, success) ->
# FUTURE TBD check for valid fn here
if !@openDBs[@dbname]
error newSQLError 'database not open'
return
@addTransaction new SQLitePluginTransaction(this, fn, error, success, true, false)
return
SQLitePlugin::readTransaction = (fn, error, success) ->
# FUTURE TBD check for valid fn here (and add test for this)
if !@openDBs[@dbname]
error newSQLError 'database not open'
return
@addTransaction new SQLitePluginTransaction(this, fn, error, success, false, true)
return
SQLitePlugin::startNextTransaction = ->
self = @
nextTick =>
if !(@dbname of @openDBs) || @openDBs[@dbname] isnt DB_STATE_OPEN
console.log 'cannot start next transaction: database not open'
return
txLock = txLocks[self.dbname]
if !txLock
console.log 'cannot start next transaction: database connection is lost'
# XXX TBD TODO (BUG #210/??): abort all pending transactions with error cb [and test!!]
# @abortAllPendingTransactions()
return
else if txLock.queue.length > 0 && !txLock.inProgress
# start next transaction in q
txLock.inProgress = true
txLock.queue.shift().start()
return
return
SQLitePlugin::abortAllPendingTransactions = ->
# extra debug info:
# if txLocks[@dbname] then console.log 'abortAllPendingTransactions with transaction queue length: ' + txLocks[@dbname].queue.length
# else console.log 'abortAllPendingTransactions with no transaction lock state'
txLock = txLocks[@dbname]
if !!txLock && txLock.queue.length > 0
# XXX TODO: what to do in case there is a (stray) transaction in progress?
#console.log 'abortAllPendingTransactions - cleanup old transaction(s)'
for tx in txLock.queue
tx.abortFromQ newSQLError 'Invalid database handle'
# XXX TODO: consider cleaning up (delete) txLocks[@dbname] resource,
# in case it is known there are no more pending transactions
txLock.queue = []
txLock.inProgress = false
return
SQLitePlugin::open = (success, error) ->
if @dbname of @openDBs
console.log 'database already open: ' + @dbname
# for a re-open run the success cb async so that the openDatabase return value
# can be used in the success handler as an alternative to the handler's
# db argument
nextTick =>
success @
return
else
console.log 'OPEN database: ' + @dbname
opensuccesscb = =>
# NOTE: the db state is NOT stored (in @openDBs) if the db was closed or deleted.
# console.log 'OPEN database: ' + @dbname + ' succeeded'
#if !@openDBs[@dbname] then call open error cb, and abort pending tx if any
if !@openDBs[@dbname]
console.log 'database was closed during open operation'
# XXX TODO [BUG #210] (and test!!):
# if !!error then error newSQLError 'database closed during open operation'
# @abortAllPendingTransactions()
if @dbname of @openDBs
@openDBs[@dbname] = DB_STATE_OPEN
if !!success then success @
txLock = txLocks[@dbname]
if !!txLock && txLock.queue.length > 0 && !txLock.inProgress
@startNextTransaction()
return
openerrorcb = =>
console.log 'OPEN database: ' + @dbname + ' failed, aborting any pending transactions'
# XXX TODO: newSQLError missing the message part!
if !!error then error newSQLError 'Could not open database'
delete @openDBs[@dbname]
@abortAllPendingTransactions()
return
# store initial DB state:
@openDBs[@dbname] = DB_STATE_INIT
cordova.exec opensuccesscb, openerrorcb, "SQLitePlugin", "open", [ @openargs ]
return
SQLitePlugin::close = (success, error) ->
if @dbname of @openDBs
if txLocks[@dbname] && txLocks[@dbname].inProgress
# XXX TBD: wait for current tx then close (??)
console.log 'cannot close: transaction is in progress'
error newSQLError 'database cannot be closed while a transaction is in progress'
return
console.log 'CLOSE database: ' + @dbname
# XXX [BUG #209] closing one db handle disables other handles to same db
delete @openDBs[@dbname]
if txLocks[@dbname] then console.log 'closing db with transaction queue length: ' + txLocks[@dbname].queue.length
else console.log 'closing db with no transaction lock state'
# XXX [BUG #210] TODO: when closing or deleting a db, abort any pending transactions [and test it!!]
cordova.exec success, error, "SQLitePlugin", "close", [ { path: @dbname } ]
else
console.log 'cannot close: database is not open'
if error then nextTick -> error()
return
SQLitePlugin::executeSql = (statement, params, success, error) ->
# XXX TODO: better to capture the result, and report it once
# the transaction has completely finished.
# This would fix BUG #204 (cannot close db in db.executeSql() callback).
mysuccess = (t, r) -> if !!success then success r
myerror = (t, e) -> if !!error then error e
myfn = (tx) ->
tx.addStatement(statement, params, mysuccess, myerror)
return
@addTransaction new SQLitePluginTransaction(this, myfn, null, null, false, false)
return
## SQLite plugin transaction object for batching:
SQLitePluginTransaction = (db, fn, error, success, txlock, readOnly) ->
# FUTURE TBD check this earlier:
if typeof(fn) != "function"
###
This is consistent with the implementation in Chrome -- it
throws if you pass anything other than a function. This also
prevents us from stalling our txQueue if somebody passes a
false value for fn.
###
throw newSQLError "transaction expected a function"
@db = db
@fn = fn
@error = error
@success = success
@txlock = txlock
@readOnly = readOnly
@executes = []
if txlock
@addStatement "BEGIN", [], null, (tx, err) ->
throw newSQLError "unable to begin transaction: " + err.message, err.code
return
SQLitePluginTransaction::start = ->
try
@fn this
@run()
catch err
# If "fn" throws, we must report the whole transaction as failed.
txLocks[@db.dbname].inProgress = false
@db.startNextTransaction()
if @error
@error newSQLError err
return
SQLitePluginTransaction::executeSql = (sql, values, success, error) ->
if @finalized
throw {message: 'InvalidStateError: DOM Exception 11: This transaction is already finalized. Transactions are committed after its success or failure handlers are called. If you are using a Promise to handle callbacks, be aware that implementations following the A+ standard adhere to run-to-completion semantics and so Promise resolution occurs on a subsequent tick and therefore after the transaction commits.', code: 11}
return
if @readOnly && READ_ONLY_REGEX.test(sql)
@handleStatementFailure(error, {message: 'invalid sql for a read-only transaction'})
return
@addStatement(sql, values, success, error)
return
# This method adds the SQL statement to the transaction queue but does not check for
# finalization since it is used to execute COMMIT and ROLLBACK.
SQLitePluginTransaction::addStatement = (sql, values, success, error) ->
params = []
if !!values && values.constructor == Array
for v in values
t = typeof v
params.push (
if v == null || v == undefined || t == 'number' || t == 'string' then v
else if v instanceof Blob then v.valueOf()
else v.toString()
)
@executes.push
success: success
error: error
sql: sql
params: params
return
SQLitePluginTransaction::handleStatementSuccess = (handler, response) ->
if !handler
return
rows = response.rows || []
payload =
rows:
item: (i) ->
rows[i]
length: rows.length
rowsAffected: response.rowsAffected or 0
insertId: response.insertId or undefined
handler this, payload
return
SQLitePluginTransaction::handleStatementFailure = (handler, response) ->
if !handler
throw newSQLError "a statement with no error handler failed: " + response.message, response.code
if handler(this, response) isnt false
throw newSQLError "a statement error callback did not return false: " + response.message, response.code
return
SQLitePluginTransaction::run = ->
txFailure = null
tropts = []
batchExecutes = @executes
waiting = batchExecutes.length
@executes = []
tx = this
handlerFor = (index, didSucceed) ->
(response) ->
try
if didSucceed
tx.handleStatementSuccess batchExecutes[index].success, response
else
tx.handleStatementFailure batchExecutes[index].error, newSQLError(response)
catch err
if !txFailure
txFailure = newSQLError(err)
if --waiting == 0
if txFailure
tx.abort txFailure
else if tx.executes.length > 0
# new requests have been issued by the callback
# handlers, so run another batch.
tx.run()
else
tx.finish()
return
i = 0
mycbmap = {}
while i < batchExecutes.length
request = batchExecutes[i]
mycbmap[i] =
success: handlerFor(i, true)
error: handlerFor(i, false)
tropts.push
qid: 1111
sql: request.sql
params: request.params
i++
mycb = (result) ->
#console.log "mycb result #{JSON.stringify result}"
last = result.length-1
for i in [0..last]
r = result[i]
type = r.type
# NOTE: r.qid can be ignored
res = r.result
q = mycbmap[i]
if q
if q[type]
q[type] res
return
cordova.exec mycb, null, "SQLitePlugin", "backgroundExecuteSqlBatch", [{dbargs: {dbname: @db.dbname}, executes: tropts}]
return
SQLitePluginTransaction::abort = (txFailure) ->
if @finalized then return
tx = @
succeeded = (tx) ->
txLocks[tx.db.dbname].inProgress = false
tx.db.startNextTransaction()
if tx.error then tx.error txFailure
return
failed = (tx, err) ->
txLocks[tx.db.dbname].inProgress = false
tx.db.startNextTransaction()
if tx.error then tx.error newSQLError("error while trying to roll back: " + err.message, err.code)
return
@finalized = true
if @txlock
@addStatement "ROLLBACK", [], succeeded, failed
@run()
else
succeeded(tx)
return
SQLitePluginTransaction::finish = ->
if @finalized then return
tx = @
succeeded = (tx) ->
txLocks[tx.db.dbname].inProgress = false
tx.db.startNextTransaction()
if tx.success then tx.success()
return
failed = (tx, err) ->
txLocks[tx.db.dbname].inProgress = false
tx.db.startNextTransaction()
if tx.error then tx.error newSQLError("error while trying to commit: " + err.message, err.code)
return
@finalized = true
if @txlock
@addStatement "COMMIT", [], succeeded, failed
@run()
else
succeeded(tx)
return
SQLitePluginTransaction::abortFromQ = (sqlerror) ->
# NOTE: since the transaction is waiting in the queue,
# the transaction function containing the SQL statements
# would not be run yet. Simply report the transaction error.
if @error
@error sqlerror
return
## SQLite plugin object factory:
dblocations = [ "docs", "libs", "nosync" ]
SQLiteFactory =
###
NOTE: this function should NOT be translated from Javascript
back to CoffeeScript by js2coffee.
If this function is edited in Javascript then someone will
have to translate it back to CoffeeScript by hand.
###
opendb: argsArray (args) ->
if args.length < 1 then return null
first = args[0]
openargs = null
okcb = null
errorcb = null
if first.constructor == String
openargs = {name: first}
if args.length >= 5
okcb = args[4]
if args.length > 5 then errorcb = args[5]
else
openargs = first
if args.length >= 2
okcb = args[1]
if args.length > 2 then errorcb = args[2]
dblocation = if !!openargs.location then dblocations[openargs.location] else null
openargs.dblocation = dblocation || dblocations[0]
if !!openargs.createFromLocation and openargs.createFromLocation == 1
openargs.createFromResource = "1"
if !!openargs.androidDatabaseImplementation and openargs.androidDatabaseImplementation == 2
openargs.androidOldDatabaseImplementation = 1
if !!openargs.androidLockWorkaround and openargs.androidLockWorkaround == 1
openargs.androidBugWorkaround = 1
new SQLitePlugin openargs, okcb, errorcb
deleteDb: (first, success, error) ->
args = {}
if first.constructor == String
#console.log "delete db name: #{first}"
args.path = first
args.dblocation = dblocations[0]
else
#console.log "delete db args: #{JSON.stringify first}"
if !(first and first['name']) then throw new Error "Please specify db name"
args.path = first.name
dblocation = if !!first.location then dblocations[first.location] else null
args.dblocation = dblocation || dblocations[0]
# XXX [BUG #210] TODO: when closing or deleting a db, abort any pending transactions (with error callback)
delete SQLitePlugin::openDBs[args.path]
cordova.exec success, error, "SQLitePlugin", "delete", [ args ]
## Exported API:
root.sqlitePlugin =
sqliteFeatures:
isSQLitePlugin: true
openDatabase: SQLiteFactory.opendb
deleteDatabase: SQLiteFactory.deleteDb
## vim directives
#### vim: set filetype=coffee :
#### vim: set expandtab :

View File

@ -0,0 +1,58 @@
# Automated cordova tests. Installs the correct cordova platform,
# installs the plugin, installs the test app, and then runs it on
# a device or emulator.
#
# usage: .\bin\test.ps1 [android|ios|windows|wp8]
#
# N.B. if you functionally change this script you _must_ change .\bin\test.sh too.
#
param([string]$platform)
if (! $platform) {
echo "usage: .\bin\test.sh [android|ios|windows|wp8]"
exit 1
}
if (! (get-command coffee) ) {
echo "you need coffeescript. please install with:"
echo "npm install -g coffee-script"
exit 1
}
if (! (get-command cordova) ) {
echo "you need cordova. please install with:"
echo "npm install -g cordova"
exit 1
}
pushd spec
if (!$?) { # run from the bin/ directory
echo "re-pushing"
pushd ../spec
}
try {
# compile coffeescript
coffee --no-header -cl -o ../www ../SQLitePlugin.coffee.md
if (!$?) {
echo "coffeescript compilation failed"
exit 1
}
echo "compiled coffeescript to javascript"
# move everything to a temp folder to avoid infinite recursion errors
if (test-path ../.plugin) {
rm -force -recurse ../.plugin -ErrorAction ignore
}
mkdir -ErrorAction ignore ../.plugin | out-null
cp -recurse ../src, ../plugin.xml, ../www ../.plugin
# update the plugin, run the test app
cordova platform add $platform
cordova plugin rm com.phonegap.plugins.sqlite
cordova plugin add ../.plugin
cordova run $platform
} finally {
popd
}

View File

@ -0,0 +1,56 @@
#!/bin/bash
#
# Automated cordova tests. Installs the correct cordova platform,
# installs the plugin, installs the test app, and then runs it on
# a device or emulator.
#
# usage: ./bin/test.sh [android|ios]
#
# N.B. if you functionally change this script you _must_ change .\bin\test.sh too.
#
# N.B. if you functionally change this script you _must_ change ./bin/test.ps1 too.
platform=$1
if [[ -z $platform ]]; then
echo "usage: ./bin/test.sh [android|ios]"
exit 1
fi
if [[ ! -x $(which coffee) ]]; then
echo "you need coffeescript. please install with:"
echo "npm install -g coffee-script"
exit 1
fi
if [[ ! -x $(which cordova) ]]; then
echo "you need cordova. please install with:"
echo "npm install -g cordova"
exit 1
fi
cd spec
if [[ $? != 0 ]]; then # run from the bin/ directory
cd ../spec
fi
# compile coffeescript
coffee --no-header -cl -o ../www ../SQLitePlugin.coffee.md
if [[ $? != 0 ]]; then
echo "coffeescript compilation failed"
exit 1
fi
echo "compiled coffeescript to javascript"
# move everything to a temp folder to avoid infinite recursion errors
rm -fr ../.plugin
mkdir -p ../.plugin
cp -r ../src ../plugin.xml ../www ../.plugin
# update the plugin, run the test app
cordova platform add $platform
cordova plugin rm com.brodysoft.sqlitePlugin
cordova plugin add ../.plugin
cordova run $platform

View File

@ -0,0 +1,21 @@
machine:
environment:
ANDROID_NDK_HOME: $ANDROID_NDK
dependencies:
pre:
- npm install -g cordova-paramedic
- npm install -g cordova
test:
pre:
- emulator -avd circleci-android21 -no-audio -no-window:
background: true
parallel: true
- circle-android wait-for-boot
override:
- cordova-paramedic --platform android --plugin .
- cp spec/www/spec/simple-test.js tests/tests.js
- cordova-paramedic --platform android --plugin . --timeout 3600000
- cp spec/www/spec/legacy.js tests/tests.js
- cordova-paramedic --platform android --plugin . --timeout 7200000

View File

@ -0,0 +1,42 @@
{
"name": "cordova-sqlite-storage",
"version": "0.7.15-pre",
"description": "Native interface to SQLite for PhoneGap/Cordova",
"cordova": {
"id": "cordova-sqlite-storage",
"platforms": [
"android",
"ios",
"windows",
"wp8",
"wp7",
"amazon-fireos"
]
},
"repository": {
"type": "git",
"url": "https://github.com/litehelpers/Cordova-sqlite-storage.git"
},
"keywords": [
"sqlite",
"ecosystem:cordova",
"cordova-android",
"cordova-ios",
"cordova-windows",
"cordova-wp8",
"cordova-wp7",
"cordova-amazon-fireos"
],
"engines": [
{
"name": "cordova",
"version": ">=3.3.0"
}
],
"author": "various",
"license": "MIT",
"bugs": {
"url": "https://github.com/litehelpers/Cordova-sqlite-storage/issues"
},
"homepage": "https://github.com/litehelpers/Cordova-sqlite-storage"
}

View File

@ -0,0 +1,298 @@
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
id="cordova-sqlite-storage"
version="0.7.15-pre">
<name>Cordova sqlite storage plugin</name>
<license>MIT</license>
<keywords>sqlite</keywords>
<description>Native interface to SQLite for PhoneGap/Cordova. Allows you to use more storage and provides more flexibility than the standard Web SQL database (window.openDatabase).</description>
<author>litehelpers (various)</author>
<engines>
<engine name="cordova" version=">=3.3.0" />
</engines>
<js-module src="www/SQLitePlugin.js" name="SQLitePlugin">
<clobbers target="SQLitePlugin" />
</js-module>
<!-- android -->
<platform name="android">
<!-- Cordova >= 3.0.0 -->
<config-file target="res/xml/config.xml" parent="/*">
<feature name="SQLitePlugin">
<param name="android-package" value="io.liteglue.SQLitePlugin"/>
</feature>
</config-file>
<source-file src="src/android/io/liteglue/SQLitePlugin.java" target-dir="src/io/liteglue"/>
<source-file src="src/android/io/liteglue/SQLiteAndroidDatabase.java" target-dir="src/io/liteglue"/>
<!-- Android-sqlite-connector: -->
<source-file src="src/android/libs/sqlite-connector.jar" target-dir="libs"/>
<!-- Android-sqlite-connector native driver libs: -->
<source-file src="src/android/libs/arm64-v8a/libsqlc-native-driver.so" target-dir="libs/arm64-v8a"/>
<source-file src="src/android/libs/armeabi/libsqlc-native-driver.so" target-dir="libs/armeabi"/>
<source-file src="src/android/libs/armeabi-v7a/libsqlc-native-driver.so" target-dir="libs/armeabi-v7a"/>
<source-file src="src/android/libs/x86/libsqlc-native-driver.so" target-dir="libs/x86"/>
<source-file src="src/android/libs/x86_64/libsqlc-native-driver.so" target-dir="libs/x86_64"/>
</platform>
<!-- ios -->
<platform name="ios">
<config-file target="config.xml" parent="/*">
<feature name="SQLitePlugin">
<param name="ios-package" value="SQLitePlugin" />
</feature>
</config-file>
<header-file src="src/ios/SQLitePlugin.h" />
<source-file src="src/ios/SQLitePlugin.m" />
<header-file src="src/common/sqlite3.h" />
<source-file src="src/common/sqlite3.c" />
</platform>
<!-- windows -->
<platform name="windows">
<js-module src="src/windows/SQLiteProxy.js" name="SQLiteProxy">
<merges target="" />
</js-module>
<!-- SQLite3 JS module from SQLite3-WinRT/SQLite3JS: -->
<js-module src="src/windows/SQLite3-WinRT/SQLite3JS/js/SQLite3.js" name="SQLite3">
<merges target="" />
</js-module>
<!-- Thanks to AllJoyn-Cordova / cordova-plugin-alljoyn: -->
<framework src="src/windows/SQLite3-WinRT/SQLite3/SQLite3.Windows/SQLite3.Windows.vcxproj" custom="true" type="projectReference" target="windows" />
<framework src="src/windows/SQLite3-WinRT/SQLite3/SQLite3.WindowsPhone/SQLite3.WindowsPhone.vcxproj" custom="true" type="projectReference" target="phone" />
</platform>
<!-- wp8 using dlls [should work but not tested on wp7]
NOTE: SQLiteWPNative.dll includes the classes from SQLite.cs
(which is not desired).
<platform name="wp8">
<config-file target="config.xml" parent="/*">
<feature name="SQLitePlugin">
<param name="wp-package" value="SQLitePlugin" />
</feature>
</config-file>
<source-file src="src/wp/SQLitePlugin.cs" />
<framework src="src/wp/cs-sqlite-dll/SQLiteWPNative.dll" custom="true" />
</platform>
-->
<platform name="wp8">
<config-file target="config.xml" parent="/*">
<feature name="SQLitePlugin">
<param name="wp-package" value="SQLitePlugin" />
</feature>
</config-file>
<source-file src="src/wp/SQLitePlugin.cs" />
<source-file src="src/wp/SQLite.cs" />
<source-file src="src/wp/csharp-sqlite-src/_Custom.cs" />
<source-file src="src/wp/csharp-sqlite-src/BtreeInt_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/Btree_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/Delegates.cs" />
<source-file src="src/wp/csharp-sqlite-src/Hash_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/VdbeInt_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/Vdbe_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/alter_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/analyze_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/attach_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/auth_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/backup_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/bitvec_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/btmutex_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/btree_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/build_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/callback_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/complete_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/crypto.cs" />
<source-file src="src/wp/csharp-sqlite-src/ctime_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/date_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/delete_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/expr_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/fault_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/fkey_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/func_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/global_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/hash_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/hwtime_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/insert_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/journal_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/keywordhash_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/legacy_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/loadext_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/main_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/malloc_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/mem_Pool.cs" />
<source-file src="src/wp/csharp-sqlite-src/memjournal_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/mutex_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/mutex_noop_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/notify_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/opcodes_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/opcodes_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/os_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/os_common_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/os_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/os_win_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/pager_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/pager_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/parse_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/parse_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/pcache1_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/pcache_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/pcache_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/pragma_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/prepare_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/printf_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/random_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/resolve_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/rowset_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/select_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/sqlite3_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/sqliteInt_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/sqliteLimit_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/status_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/table_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/tokenize_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/trigger_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/update_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/utf_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/util_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/vacuum_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/vdbe_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/vdbeapi_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/vdbeaux_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/vdbeblob_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/vdbemem_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/vdbetrace_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/vtab_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/wal_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/wal_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/walker_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/where_c.cs" />
</platform>
<platform name="wp7">
<config-file target="config.xml" parent="/*">
<feature name="SQLitePlugin">
<param name="wp-package" value="SQLitePlugin" />
</feature>
</config-file>
<source-file src="src/wp/SQLitePlugin.cs" />
<source-file src="src/wp/SQLite.cs" />
<source-file src="src/wp/csharp-sqlite-src/_Custom.cs" />
<source-file src="src/wp/csharp-sqlite-src/BtreeInt_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/Btree_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/Delegates.cs" />
<source-file src="src/wp/csharp-sqlite-src/Hash_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/VdbeInt_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/Vdbe_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/alter_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/analyze_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/attach_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/auth_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/backup_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/bitvec_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/btmutex_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/btree_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/build_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/callback_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/complete_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/crypto.cs" />
<source-file src="src/wp/csharp-sqlite-src/ctime_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/date_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/delete_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/expr_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/fault_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/fkey_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/func_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/global_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/hash_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/hwtime_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/insert_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/journal_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/keywordhash_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/legacy_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/loadext_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/main_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/malloc_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/mem_Pool.cs" />
<source-file src="src/wp/csharp-sqlite-src/memjournal_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/mutex_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/mutex_noop_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/notify_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/opcodes_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/opcodes_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/os_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/os_common_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/os_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/os_win_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/pager_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/pager_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/parse_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/parse_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/pcache1_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/pcache_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/pcache_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/pragma_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/prepare_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/printf_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/random_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/resolve_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/rowset_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/select_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/sqlite3_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/sqliteInt_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/sqliteLimit_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/status_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/table_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/tokenize_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/trigger_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/update_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/utf_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/util_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/vacuum_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/vdbe_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/vdbeapi_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/vdbeaux_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/vdbeblob_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/vdbemem_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/vdbetrace_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/vtab_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/wal_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/wal_h.cs" />
<source-file src="src/wp/csharp-sqlite-src/walker_c.cs" />
<source-file src="src/wp/csharp-sqlite-src/where_c.cs" />
</platform>
<!-- FUTURE TBD: fix and test for amazon-fireos -->
</plugin>
<!-- vim: set expandtab : -->

View File

@ -0,0 +1,4 @@
{
"id": "com.phonegap.plugins.sqlite.tests",
"name": "Cordova SQLite Plugin Test Runner"
}

Some files were not shown because too many files have changed in this diff Show More