diff --git a/onsenui2/.gitignore b/onsenui2/.gitignore
new file mode 100644
index 00000000..2009598a
--- /dev/null
+++ b/onsenui2/.gitignore
@@ -0,0 +1,32 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+
+# Dependency directory
+node_modules
+
+# Optional npm cache directory
+.npm
+
+# Optional REPL history
+.node_repl_history
+
+### OSX ###
+*.DS_Store
+.AppleDouble
+.LSOverride
+
+# Thumbnails
+._*
+
+### NPM scripts ###
+www/assets/angular
+www/assets/localforage
+www/assets/onsenui
+www/assets/lodash
+
+### cordova ###
+/platforms
+/plugins
+/hooks
diff --git a/onsenui2/.npmignore b/onsenui2/.npmignore
new file mode 100644
index 00000000..0d900251
--- /dev/null
+++ b/onsenui2/.npmignore
@@ -0,0 +1,2 @@
+# Mac
+.DS_Store
diff --git a/onsenui2/README.md b/onsenui2/README.md
new file mode 100644
index 00000000..77457f2e
--- /dev/null
+++ b/onsenui2/README.md
@@ -0,0 +1,52 @@
+# Onsen UI 2 implementation with Angular 1
+Visit [Onsen UI page](https://onsen.io/)
+
+##Introduction
+
+Onsen UI provides UI framework and tools for creating fast and beautiful HTML5 hybrid mobile apps based on PhoneGap/Cordova. Having common core with no framework dependencies, app development with Onsen UI is easy with any of the ever-changing JavaScript frameworks.
+
+##Building the Application
+First follow [Apache Cordova](https://cordova.apache.org/#getstarted) or [PhoneGap](http://docs.phonegap.com/getting-started/1-install-phonegap/cli/) install instrucctions.
+
+Then install the needed NPM packages:
+
+```
+$ npm install
+```
+
+And setup project:
+
+```
+$ npm run setup
+```
+
+###Add your chosen platform
+
+```
+$ phonegap platform add android
+$ phonegap platform add ios
+```
+
+###Develop
+
+```
+$ phonegap serve
+```
+
+###Build
+
+```
+$ phonegap build android
+$ phonegap build ios
+```
+
+##Application Structure
++ `package.json`
++ `config.xml` - Cordova/PhoneGap configuration file
++ `\www`
+ + `index.html` - Entry HTML for app.
+ + `\res` - icons and splashscreens used by the app.
+ + `\services` - Services to handle the communication to the Nestoria APIs and persist recent searches and favourites.
+ + `\pages` - Includes a folder for each app page (view and logic).
+ + `\assets` - Vendor and Custom JavaScript and styles.
+
diff --git a/onsenui2/config.xml b/onsenui2/config.xml
new file mode 100644
index 00000000..f00d15f8
--- /dev/null
+++ b/onsenui2/config.xml
@@ -0,0 +1,87 @@
+
+
+ PropertyCross
+
+ PropertyCross Implementation With Onsen UI 2 - Angular 1
+
+ Leonel Viera
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/onsenui2/npm-shrinkwrap.json b/onsenui2/npm-shrinkwrap.json
new file mode 100644
index 00000000..8fd56d97
--- /dev/null
+++ b/onsenui2/npm-shrinkwrap.json
@@ -0,0 +1,664 @@
+{
+ "name": "propertycross-onsenui2-implementation",
+ "version": "4.0.2",
+ "dependencies": {
+ "acorn": {
+ "version": "1.2.2",
+ "from": "acorn@>=1.0.3 <2.0.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-1.2.2.tgz"
+ },
+ "amdefine": {
+ "version": "1.0.0",
+ "from": "amdefine@>=0.0.4",
+ "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz"
+ },
+ "angular": {
+ "version": "1.5.8",
+ "from": "angular@1.5.8",
+ "resolved": "https://registry.npmjs.org/angular/-/angular-1.5.8.tgz"
+ },
+ "ansi-regex": {
+ "version": "2.0.0",
+ "from": "ansi-regex@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "2.2.1",
+ "from": "ansi-styles@>=2.2.1 <3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "dev": true
+ },
+ "array-filter": {
+ "version": "0.0.1",
+ "from": "array-filter@>=0.0.0 <0.1.0",
+ "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz",
+ "dev": true
+ },
+ "array-map": {
+ "version": "0.0.0",
+ "from": "array-map@>=0.0.0 <0.1.0",
+ "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz",
+ "dev": true
+ },
+ "array-reduce": {
+ "version": "0.0.0",
+ "from": "array-reduce@>=0.0.0 <0.1.0",
+ "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz",
+ "dev": true
+ },
+ "ast-types": {
+ "version": "0.8.15",
+ "from": "ast-types@0.8.15",
+ "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.8.15.tgz"
+ },
+ "balanced-match": {
+ "version": "0.4.2",
+ "from": "balanced-match@>=0.4.1 <0.5.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
+ "dev": true
+ },
+ "base62": {
+ "version": "0.1.1",
+ "from": "base62@0.1.1",
+ "resolved": "https://registry.npmjs.org/base62/-/base62-0.1.1.tgz"
+ },
+ "brace-expansion": {
+ "version": "1.1.6",
+ "from": "brace-expansion@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz",
+ "dev": true
+ },
+ "builtin-modules": {
+ "version": "1.1.1",
+ "from": "builtin-modules@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "from": "chalk@>=1.1.3 <2.0.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "from": "concat-map@0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "dev": true
+ },
+ "copyfiles": {
+ "version": "1.0.0",
+ "from": "copyfiles@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-1.0.0.tgz",
+ "dev": true,
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "from": "isarray@>=1.0.0 <1.1.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "2.0.6",
+ "from": "readable-stream@>=2.0.0 <2.1.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
+ "dev": true,
+ "dependencies": {
+ "process-nextick-args": {
+ "version": "1.0.7",
+ "from": "process-nextick-args@>=1.0.6 <1.1.0",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
+ "dev": true
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "from": "util-deprecate@>=1.0.1 <1.1.0",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "dev": true
+ }
+ }
+ },
+ "through2": {
+ "version": "2.0.1",
+ "from": "through2@>=2.0.1 <3.0.0",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.1.tgz",
+ "dev": true
+ }
+ }
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "from": "core-util-is@>=1.0.0 <1.1.0",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz"
+ },
+ "cross-spawn": {
+ "version": "4.0.2",
+ "from": "cross-spawn@>=4.0.0 <5.0.0",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz",
+ "dev": true
+ },
+ "define-properties": {
+ "version": "1.1.2",
+ "from": "define-properties@>=1.1.2 <2.0.0",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
+ "dev": true
+ },
+ "duplexer": {
+ "version": "0.1.1",
+ "from": "duplexer@>=0.1.1 <0.2.0",
+ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
+ "dev": true
+ },
+ "error-ex": {
+ "version": "1.3.0",
+ "from": "error-ex@>=1.2.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.0.tgz",
+ "dev": true
+ },
+ "es-abstract": {
+ "version": "1.6.1",
+ "from": "es-abstract@>=1.4.3 <2.0.0",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.6.1.tgz",
+ "dev": true
+ },
+ "es-to-primitive": {
+ "version": "1.1.1",
+ "from": "es-to-primitive@>=1.1.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz",
+ "dev": true
+ },
+ "es3ify": {
+ "version": "0.1.4",
+ "from": "es3ify@>=0.1.3 <0.2.0",
+ "resolved": "https://registry.npmjs.org/es3ify/-/es3ify-0.1.4.tgz"
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "from": "escape-string-regexp@>=1.0.2 <2.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "dev": true
+ },
+ "esmangle-evaluator": {
+ "version": "1.0.1",
+ "from": "esmangle-evaluator@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/esmangle-evaluator/-/esmangle-evaluator-1.0.1.tgz"
+ },
+ "esprima-fb": {
+ "version": "3001.1.0-dev-harmony-fb",
+ "from": "esprima-fb@>=3001.1.0-dev-harmony-fb <3001.2.0",
+ "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-3001.0001.0000-dev-harmony-fb.tgz"
+ },
+ "event-stream": {
+ "version": "3.3.4",
+ "from": "event-stream@>=3.3.0 <3.4.0",
+ "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz",
+ "dev": true
+ },
+ "falafel": {
+ "version": "1.2.0",
+ "from": "falafel@>=1.0.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/falafel/-/falafel-1.2.0.tgz"
+ },
+ "find-up": {
+ "version": "1.1.2",
+ "from": "find-up@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+ "dev": true
+ },
+ "foreach": {
+ "version": "2.0.5",
+ "from": "foreach@>=2.0.5 <3.0.0",
+ "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz"
+ },
+ "from": {
+ "version": "0.1.3",
+ "from": "from@>=0.0.0 <1.0.0",
+ "resolved": "https://registry.npmjs.org/from/-/from-0.1.3.tgz",
+ "dev": true
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "from": "fs.realpath@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "dev": true
+ },
+ "function-bind": {
+ "version": "1.1.0",
+ "from": "function-bind@>=1.0.2 <2.0.0",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.1",
+ "from": "glob@>=7.0.5 <8.0.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz",
+ "dev": true
+ },
+ "graceful-fs": {
+ "version": "4.1.9",
+ "from": "graceful-fs@>=4.1.2 <5.0.0",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.9.tgz",
+ "dev": true
+ },
+ "has-ansi": {
+ "version": "2.0.0",
+ "from": "has-ansi@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "dev": true
+ },
+ "hosted-git-info": {
+ "version": "2.1.5",
+ "from": "hosted-git-info@>=2.1.4 <3.0.0",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.1.5.tgz",
+ "dev": true
+ },
+ "immediate": {
+ "version": "3.0.6",
+ "from": "immediate@>=3.0.5 <3.1.0",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz"
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "from": "inflight@>=1.0.4 <2.0.0",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "dev": true
+ },
+ "inherits": {
+ "version": "2.0.1",
+ "from": "inherits@>=2.0.1 <2.1.0",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "inline-process-browser": {
+ "version": "1.0.0",
+ "from": "inline-process-browser@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/inline-process-browser/-/inline-process-browser-1.0.0.tgz"
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "from": "is-arrayish@>=0.2.1 <0.3.0",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "dev": true
+ },
+ "is-builtin-module": {
+ "version": "1.0.0",
+ "from": "is-builtin-module@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
+ "dev": true
+ },
+ "is-callable": {
+ "version": "1.1.3",
+ "from": "is-callable@>=1.1.3 <2.0.0",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz",
+ "dev": true
+ },
+ "is-date-object": {
+ "version": "1.0.1",
+ "from": "is-date-object@>=1.0.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
+ "dev": true
+ },
+ "is-regex": {
+ "version": "1.0.3",
+ "from": "is-regex@>=1.0.3 <2.0.0",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.3.tgz",
+ "dev": true
+ },
+ "is-symbol": {
+ "version": "1.0.1",
+ "from": "is-symbol@>=1.0.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz",
+ "dev": true
+ },
+ "is-utf8": {
+ "version": "0.2.1",
+ "from": "is-utf8@>=0.2.0 <0.3.0",
+ "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+ "dev": true
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "from": "isarray@0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
+ },
+ "isexe": {
+ "version": "1.1.2",
+ "from": "isexe@>=1.1.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-1.1.2.tgz",
+ "dev": true
+ },
+ "jsonify": {
+ "version": "0.0.0",
+ "from": "jsonify@>=0.0.0 <0.1.0",
+ "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
+ "dev": true
+ },
+ "jstransform": {
+ "version": "3.0.0",
+ "from": "jstransform@>=3.0.0 <3.1.0",
+ "resolved": "https://registry.npmjs.org/jstransform/-/jstransform-3.0.0.tgz"
+ },
+ "lie": {
+ "version": "3.0.2",
+ "from": "lie@3.0.2",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.0.2.tgz"
+ },
+ "load-json-file": {
+ "version": "1.1.0",
+ "from": "load-json-file@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+ "dev": true
+ },
+ "localforage": {
+ "version": "1.4.3",
+ "from": "localforage@1.4.3",
+ "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.4.3.tgz"
+ },
+ "lodash": {
+ "version": "4.16.4",
+ "from": "lodash@4.16.4",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.16.4.tgz"
+ },
+ "lru-cache": {
+ "version": "4.0.1",
+ "from": "lru-cache@>=4.0.1 <5.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.1.tgz",
+ "dev": true
+ },
+ "ltcdr": {
+ "version": "2.2.1",
+ "from": "ltcdr@>=2.2.1 <3.0.0",
+ "resolved": "https://registry.npmjs.org/ltcdr/-/ltcdr-2.2.1.tgz",
+ "dev": true
+ },
+ "map-stream": {
+ "version": "0.1.0",
+ "from": "map-stream@>=0.1.0 <0.2.0",
+ "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.0.3",
+ "from": "minimatch@>=3.0.2 <4.0.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz",
+ "dev": true
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "from": "minimist@0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "from": "mkdirp@>=0.5.1 <0.6.0",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "dev": true
+ },
+ "noms": {
+ "version": "0.0.0",
+ "from": "noms@0.0.0",
+ "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz",
+ "dev": true
+ },
+ "normalize-package-data": {
+ "version": "2.3.5",
+ "from": "normalize-package-data@>=2.3.2 <3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.3.5.tgz",
+ "dev": true
+ },
+ "npm-run-all": {
+ "version": "2.3.0",
+ "from": "npm-run-all@>=2.3.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-2.3.0.tgz",
+ "dev": true
+ },
+ "object-assign": {
+ "version": "4.1.0",
+ "from": "object-assign@>=4.0.1 <5.0.0",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz",
+ "dev": true
+ },
+ "object-keys": {
+ "version": "1.0.9",
+ "from": "object-keys@>=1.0.6 <2.0.0",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.9.tgz"
+ },
+ "once": {
+ "version": "1.4.0",
+ "from": "once@>=1.3.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "dev": true
+ },
+ "onsenui": {
+ "version": "2.0.3",
+ "from": "onsenui@2.0.3",
+ "resolved": "https://registry.npmjs.org/onsenui/-/onsenui-2.0.3.tgz"
+ },
+ "parse-json": {
+ "version": "2.2.0",
+ "from": "parse-json@>=2.2.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "dev": true
+ },
+ "path-exists": {
+ "version": "2.1.0",
+ "from": "path-exists@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "from": "path-is-absolute@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "dev": true
+ },
+ "path-type": {
+ "version": "1.1.0",
+ "from": "path-type@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
+ "dev": true
+ },
+ "pause-stream": {
+ "version": "0.0.11",
+ "from": "pause-stream@0.0.11",
+ "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
+ "dev": true
+ },
+ "pify": {
+ "version": "2.3.0",
+ "from": "pify@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "dev": true
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "from": "pinkie@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "dev": true
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "from": "pinkie-promise@>=2.0.1 <3.0.0",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "dev": true
+ },
+ "private": {
+ "version": "0.1.6",
+ "from": "private@>=0.1.5 <0.2.0",
+ "resolved": "https://registry.npmjs.org/private/-/private-0.1.6.tgz"
+ },
+ "ps-tree": {
+ "version": "1.1.0",
+ "from": "ps-tree@>=1.0.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.1.0.tgz",
+ "dev": true
+ },
+ "pseudomap": {
+ "version": "1.0.2",
+ "from": "pseudomap@>=1.0.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "dev": true
+ },
+ "read-pkg": {
+ "version": "1.1.0",
+ "from": "read-pkg@>=1.1.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
+ "dev": true
+ },
+ "read-pkg-up": {
+ "version": "1.0.1",
+ "from": "read-pkg-up@>=1.0.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "1.0.34",
+ "from": "readable-stream@>=1.0.33-1 <1.1.0-0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz"
+ },
+ "recast": {
+ "version": "0.10.43",
+ "from": "recast@>=0.10.1 <0.11.0",
+ "resolved": "https://registry.npmjs.org/recast/-/recast-0.10.43.tgz",
+ "dependencies": {
+ "esprima-fb": {
+ "version": "15001.1001.0-dev-harmony-fb",
+ "from": "esprima-fb@>=15001.1001.0-dev-harmony-fb <15001.1002.0",
+ "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz"
+ },
+ "source-map": {
+ "version": "0.5.6",
+ "from": "source-map@>=0.5.0 <0.6.0",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz"
+ }
+ }
+ },
+ "rimraf": {
+ "version": "2.5.4",
+ "from": "rimraf@>=2.5.4 <3.0.0",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.3.0",
+ "from": "semver@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0||>=4.0.0 <5.0.0||>=5.0.0 <6.0.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
+ "dev": true
+ },
+ "shell-quote": {
+ "version": "1.6.1",
+ "from": "shell-quote@>=1.6.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.1.31",
+ "from": "source-map@0.1.31",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.31.tgz"
+ },
+ "spdx-correct": {
+ "version": "1.0.2",
+ "from": "spdx-correct@>=1.0.0 <1.1.0",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz",
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "1.0.4",
+ "from": "spdx-expression-parse@>=1.0.0 <1.1.0",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz",
+ "dev": true
+ },
+ "spdx-license-ids": {
+ "version": "1.2.2",
+ "from": "spdx-license-ids@>=1.0.2 <2.0.0",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz",
+ "dev": true
+ },
+ "split": {
+ "version": "0.3.3",
+ "from": "split@>=0.3.0 <0.4.0",
+ "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz",
+ "dev": true
+ },
+ "stream-combiner": {
+ "version": "0.0.4",
+ "from": "stream-combiner@>=0.0.4 <0.1.0",
+ "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz",
+ "dev": true
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "from": "string_decoder@>=0.10.0 <0.11.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
+ },
+ "string.prototype.padend": {
+ "version": "3.0.0",
+ "from": "string.prototype.padend@>=3.0.0 <4.0.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "from": "strip-ansi@>=3.0.0 <4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "dev": true
+ },
+ "strip-bom": {
+ "version": "2.0.0",
+ "from": "strip-bom@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "from": "supports-color@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "dev": true
+ },
+ "through": {
+ "version": "2.3.8",
+ "from": "through@>=2.3.4 <2.4.0",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
+ },
+ "through2": {
+ "version": "0.6.5",
+ "from": "through2@>=0.6.5 <0.7.0",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz"
+ },
+ "unreachable-branch-transform": {
+ "version": "0.3.0",
+ "from": "unreachable-branch-transform@>=0.3.0 <0.4.0",
+ "resolved": "https://registry.npmjs.org/unreachable-branch-transform/-/unreachable-branch-transform-0.3.0.tgz"
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.1",
+ "from": "validate-npm-package-license@>=3.0.1 <4.0.0",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz",
+ "dev": true
+ },
+ "which": {
+ "version": "1.2.11",
+ "from": "which@>=1.2.9 <2.0.0",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.2.11.tgz",
+ "dev": true
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "from": "wrappy@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "dev": true
+ },
+ "xtend": {
+ "version": "4.0.1",
+ "from": "xtend@>=4.0.0 <4.1.0-0",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz"
+ },
+ "yallist": {
+ "version": "2.0.0",
+ "from": "yallist@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.0.0.tgz",
+ "dev": true
+ }
+ }
+}
diff --git a/onsenui2/package.json b/onsenui2/package.json
new file mode 100644
index 00000000..0cf41250
--- /dev/null
+++ b/onsenui2/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "propertycross-onsenui2-implementation",
+ "version": "1.0.0",
+ "description": "An implementation of PropertyCross using onsen ui 2",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/vieraleonel/onsenui2-propertycross-impl"
+ },
+ "author": "Viera Leonel",
+ "license": "SEE LICENCE IN LICENSE",
+ "keywords": [
+ "cordova",
+ "propertycross",
+ "phonegap",
+ "framework7",
+ "mobile",
+ "hybrid"
+],
+ "homepage": "https://github.com/vieraleonel/onsenui2-propertycross-impl",
+ "dependencies": {
+ "angular": "^1.5.6",
+ "localforage": "^1.4.2",
+ "lodash": "^4.13.1",
+ "onsenui": "^2.0.3"
+ },
+ "devDependencies": {
+ "copyfiles": "^1.0.0",
+ "npm-run-all": "^2.3.0",
+ "rimraf": "^2.5.4"
+ },
+ "scripts": {
+ "setup:localforage": "copyfiles -f ./node_modules/localforage/dist/* ./www/assets/localforage",
+ "setup:lodash": "copyfiles -f ./node_modules/lodash/lodash.min.js ./www/assets/lodash",
+ "setup:angular": "copyfiles -f ./node_modules/angular/angular-csp.css ./node_modules/angular/angular.min.js ./node_modules/angular/angular.min.js.map ./www/assets/angular",
+ "setup:onsenui": "copyfiles -u 1 './node_modules/onsenui/css/**' './node_modules/onsenui/js/**' ./www/assets",
+ "setup": "npm-run-all --parallel 'setup:localforage' 'setup:lodash' 'setup:angular' 'setup:onsenui'",
+ "clean": "rimraf ./www/assets/localforage ./www/assets/lodash ./www/assets/angular ./www/assets/onsenui"
+ }
+}
diff --git a/onsenui2/www/assets/css/styles.css b/onsenui2/www/assets/css/styles.css
new file mode 100644
index 00000000..458824a7
--- /dev/null
+++ b/onsenui2/www/assets/css/styles.css
@@ -0,0 +1,66 @@
+/* Navbar */
+.navigation-bar .right {
+ padding-right: 10px;
+}
+
+body.material .navbar-blue {
+ background-color: #387ef5;
+}
+
+/* Search input */
+body.ios #search-properties-container {
+ background-color: white;
+ border-top: 1px solid #d2cece;
+ border-bottom: 1px solid #d2cece;
+}
+
+#search-properties-container {
+ margin: 15px 0 15px 0;
+}
+
+#search-properties-container ons-input {
+ width:100%
+}
+
+body.ios #search-properties-container input {
+ height: 50px;
+}
+
+/* Badges */
+.badge {
+ display: inline-block;
+ min-width: 10px;
+ padding: 3px 7px;
+ font-size: 12px;
+ font-weight: 700;
+ line-height: 1;
+ color: #fff;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ background-color: #777;
+ border-radius: 10px;
+}
+
+/* Buttons */
+body.material .btn-blue {
+ background-color: #387ef5;
+}
+
+body.ios .btn-green {
+ background-color: #4cd964;
+}
+
+/* Favs */
+body.material .fav-pink {
+ background-color: #E91E63;
+}
+
+/* Errors */
+body.ios .text-error {
+ color: #ff3b30;
+}
+
+body.material .text-error {
+ color: #d50000;
+}
\ No newline at end of file
diff --git a/onsenui2/www/assets/js/constants.js b/onsenui2/www/assets/js/constants.js
new file mode 100644
index 00000000..b5aa86b9
--- /dev/null
+++ b/onsenui2/www/assets/js/constants.js
@@ -0,0 +1,24 @@
+/**
+ * Global constants
+ * @return {[type]} [description]
+ */
+(function() {
+ 'use strict';
+
+ app.constant('PROPERTY_API', {
+ baseUrl: 'http://api.nestoria.co.uk/api?country=uk&pretty=0&action=search_listings&encoding=json&listing_type=buy',
+ error: {
+ messages: {
+ ZERO_PROPERTIES: 'There were no properties found for the given location.',
+ GENERIC_ERROR: 'An error occurred while searching. Please check your network connection and try again.',
+ LOCATION_DISABLED: 'The use of location is currently disabled.',
+ LOCATION_UNAVAILABLE: 'Unable to detect current location. Please ensure location is turned on in your phone settings and try again.'
+ }
+ },
+ status: {
+ SUCCESS: 1,
+ AMBIGUOUS: 2,
+ ERROR: 0
+ }
+ });
+})(window.app);
\ No newline at end of file
diff --git a/onsenui2/www/icon.png b/onsenui2/www/icon.png
new file mode 100644
index 00000000..2cb89282
Binary files /dev/null and b/onsenui2/www/icon.png differ
diff --git a/onsenui2/www/index.html b/onsenui2/www/index.html
new file mode 100755
index 00000000..08a60e94
--- /dev/null
+++ b/onsenui2/www/index.html
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ PropertyCross
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/onsenui2/www/pages/favourites/favourites.controller.js b/onsenui2/www/pages/favourites/favourites.controller.js
new file mode 100644
index 00000000..e366fbea
--- /dev/null
+++ b/onsenui2/www/pages/favourites/favourites.controller.js
@@ -0,0 +1,49 @@
+(function(app){
+ 'use strict';
+
+ // add controller to app
+ app.controller('FavouritesPageController', FavouritesPageController);
+
+ // controller dependencies
+ FavouritesPageController.$inject = ['FavouritesService', '$timeout', '$window']
+
+ // controller
+ function FavouritesPageController(FavouritesService, $timeout, $window) {
+ var vm = this;
+
+ // properties
+ vm.faves = [];
+
+ // methods
+ vm.goToPropertyDetails = goToPropertyDetails;
+
+ // startup method
+ activate();
+ ////////////////////////
+
+ /**
+ * Startup method. Get favourites from service
+ */
+ function activate() {
+ FavouritesService.getAll()
+ .then(function(faves) {
+ // workarount to force $digest cycle
+ $timeout(function() { vm.faves = faves;}, 0)
+ });
+ }
+
+ /**
+ * Navigates to property details page with selected favourite
+ *
+ * @param Object property Selected property
+ */
+ function goToPropertyDetails(property) {
+ // navi is global from onsen-navigator
+ $window.navi.pushPage('pages/property-details/property-details.html', {
+ data: {
+ selectedProperty: property
+ }
+ });
+ }
+ } // function FavouritesPageController
+})(window.app);
\ No newline at end of file
diff --git a/onsenui2/www/pages/favourites/favourites.html b/onsenui2/www/pages/favourites/favourites.html
new file mode 100644
index 00000000..eb3ffffd
--- /dev/null
+++ b/onsenui2/www/pages/favourites/favourites.html
@@ -0,0 +1,27 @@
+
+
+
+ Back
+
+ Favourites
+
+
+
+
+
+ You have not added any properties to your favourites
+
+
+
+
+
+
+
![]()
+
+
+ {{ ::property.price_formatted }}
+ {{ ::property.title }}
+
+
+
+
diff --git a/onsenui2/www/pages/home/home.controller.js b/onsenui2/www/pages/home/home.controller.js
new file mode 100644
index 00000000..d7a38f8c
--- /dev/null
+++ b/onsenui2/www/pages/home/home.controller.js
@@ -0,0 +1,185 @@
+(function(app){
+ 'use strict';
+
+ // add controller to app
+ app.controller('HomePageController', HomePageController);
+
+ // controller dependencies
+ HomePageController.$inject = ['PROPERTY_API', 'PropertiesService', 'RecentSearchesService', '$window', '$timeout']
+
+ // controller
+ function HomePageController(PROPERTY_API, PropertiesService, RecentSearchesService, $window, $timeout) {
+ var vm = this;
+
+ // properties
+ vm.searchTerm = '';
+ vm.loading = false;
+ vm.recentSearches = [];
+ vm.state = 1;
+ vm.errorMessage = '';
+ vm.locations = [];
+
+ // methods
+ vm.searchByTerm = searchByTerm;
+ vm.searchByPosition = searchByPosition;
+ vm.doSearchWith = doSearchWith;
+ vm.goToFaves = goToFaves;
+
+ // startup method
+ activate();
+ ////////////////////////
+
+ /**
+ * Startup method
+ */
+ function activate() {
+ // listener for getting recent searches every time home page is shown
+ $window.document
+ .addEventListener('show', function(event){
+ if (event.srcElement.id === 'home-page') {
+ getRecentSearches();
+ }
+ });
+ }
+
+ /**
+ * Get recent searches form storage
+ */
+ function getRecentSearches() {
+ RecentSearchesService.get()
+ .then(function(data) {
+ // workarount to force $digest cycle
+ $timeout(function() {vm.recentSearches = data;}, 0)
+ });
+ }
+
+ /**
+ * Toggle Loading modal visibility
+ */
+ function toggleModal() {
+ $window.document
+ .getElementById('home-modal')
+ .toggle();
+ }
+
+ /**
+ * Performs a search by term
+ */
+ function searchByTerm() {
+ toggleModal();
+
+ // do search
+ PropertiesService.searchByTerm(vm.searchTerm)
+ .then(function(data) {
+ processResponse(data);
+ })
+ .catch(function(){
+ showErrorState({message: 'Other error'});
+ });
+ }
+
+ /**
+ * Process Successfull data response from service
+ *
+ * @param Object data Returned service response
+ */
+ function processResponse(data) {
+ if (data.status === PROPERTY_API.status.SUCCESS) {
+ showResultsPage(data);
+ }
+ else if (data.status === PROPERTY_API.status.AMBIGUOUS) {
+ showLocationsState(data);
+ }
+ else { // error
+ showErrorState(data);
+ }
+ }
+
+ /**
+ * Performs a search by current position
+ */
+ function searchByPosition() {
+ vm.searchTerm = '';
+
+ toggleModal();
+
+ PropertiesService.searchByPosition()
+ .then(function(data){
+ processResponse(data);
+ })
+ .catch(function(error){
+ showErrorState(error);
+ });
+ }
+
+ /**
+ * Navigates to results page
+ *
+ * @param Object data
+ */
+ function showResultsPage(data) {
+ vm.state = 1;
+ vm.locations = [];
+ vm.errorMessage = '';
+
+ // if search was by term, stores it in recent searches
+ if (vm.searchTerm !== '') {
+ RecentSearchesService.store({
+ term: vm.searchTerm,
+ results: data.results
+ });
+ }
+
+ $window.navi
+ .pushPage('pages/results/results.html')
+ .then(function(){
+ toggleModal();
+ });
+ }
+
+ /**
+ * Shows user a list to select a location
+ *
+ * @param Object data
+ */
+ function showLocationsState(data) {
+ vm.state = 2; // ambiguous
+ vm.locations = data.locations;
+
+ toggleModal();
+ }
+
+ /**
+ * Shows user a message for an error situation
+ *
+ * @param Object data
+ */
+ function showErrorState(data) {
+ vm.state = 0; // error
+ vm.errorMessage = data.message;
+
+ toggleModal();
+ }
+
+ /**
+ * Perform a term based search with de selected location (from location state)
+ *
+ * @param Object location
+ */
+ function doSearchWith(location) {
+ vm.state = 1;
+ vm.locations = [];
+ vm.errorMessage = '';
+ vm.searchTerm = location;
+
+ searchByTerm();
+ }
+
+ /**
+ * Navigates to favourites pages
+ */
+ function goToFaves() {
+ $window.navi.pushPage('pages/favourites/favourites.html');
+ }
+ } // function HomePageController
+})(window.app);
\ No newline at end of file
diff --git a/onsenui2/www/pages/home/home.html b/onsenui2/www/pages/home/home.html
new file mode 100644
index 00000000..ef100514
--- /dev/null
+++ b/onsenui2/www/pages/home/home.html
@@ -0,0 +1,64 @@
+
+
+ PropertyCross
+ Faves
+
+
+
+
+
+ Use the form below to search for houses to buy. You can search by place-name, postcode, or click 'My location', to search in your current location!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Go
+
+
+
+ My location
+
+
+
+
+
+
+ {{ vm.errorMessage }}
+
+
+
+
+ Recent searches:
+
+
+ {{ search.term }}
+
+
+ {{ search.results }}
+
+
+
+
+
+ Please select a location below:
+
+ {{ location.title }}
+
+
+
+
diff --git a/onsenui2/www/pages/property-details/property-details.controller.js b/onsenui2/www/pages/property-details/property-details.controller.js
new file mode 100644
index 00000000..26421240
--- /dev/null
+++ b/onsenui2/www/pages/property-details/property-details.controller.js
@@ -0,0 +1,81 @@
+(function(app){
+ 'use strict';
+
+ // add controller to app
+ app.controller('PropertyDetailsPageController', PropertyDetailsPageController);
+
+ // controller dependencies
+ PropertyDetailsPageController.$inject = ['PropertiesService', 'FavouritesService', '$timeout', '$window']
+
+ // controller
+ function PropertyDetailsPageController(PropertiesService, FavouritesService, $timeout, $window) {
+ var vm = this;
+
+ // properties
+ vm.property = {};
+
+ // methods
+ vm.toggleFav = toggleFav;
+
+ // startup method
+ activate();
+ ////////////////////////
+
+ /**
+ * Startup method. Get property from
+ */
+ function activate() {
+ // navi is global from onsen-navigator
+ vm.property = prepareProperty($window.navi.topPage.data.selectedProperty);
+ }
+
+ /**
+ * Transforms property title and check for favourite state
+ *
+ * @param Object property
+ * @return Object Prepared property
+ */
+ function prepareProperty(property) {
+ property.title = formatTitle(property.title);
+
+ FavouritesService.isFav(property)
+ .then(function(isFav) {
+ // workarount to force $digest cycle
+ $timeout(function() {vm.property.isFav = isFav;}, 0)
+ });
+
+ return property;
+ }
+
+ /**
+ * Take only first and second title parts
+ *
+ * @param string title
+ * @return string Formatted title
+ */
+ function formatTitle(title) {
+ var splitTitle = title.split(',');
+
+ if (splitTitle.length > 1) {
+ title = splitTitle[0] + ', ' + splitTitle[1];
+ } else {
+ title = splitTitle[0];
+ }
+
+ return title;
+ }
+
+ /**
+ * Toggles fav state
+ */
+ function toggleFav() {
+ if (vm.property.isFav) {
+ FavouritesService.remove(vm.property);
+ } else {
+ FavouritesService.store(vm.property);
+ }
+
+ vm.property.isFav = !vm.property.isFav;
+ }
+ }
+})(window.app);
\ No newline at end of file
diff --git a/onsenui2/www/pages/property-details/property-details.html b/onsenui2/www/pages/property-details/property-details.html
new file mode 100644
index 00000000..e5f0bfdd
--- /dev/null
+++ b/onsenui2/www/pages/property-details/property-details.html
@@ -0,0 +1,44 @@
+
+
+
+ Back
+
+ Property Details
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ ::vm.property.price_formatted }}
+ {{ ::vm.property.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ ::vm.property.bedroom_number }} beeds, {{ ::vm.property.bathroom_number }} bathrooms
+ {{ ::vm.property.summary }}
+
+
+
+
+
+
+
+
+
diff --git a/onsenui2/www/pages/results/results.controller.js b/onsenui2/www/pages/results/results.controller.js
new file mode 100644
index 00000000..65571b8c
--- /dev/null
+++ b/onsenui2/www/pages/results/results.controller.js
@@ -0,0 +1,85 @@
+(function(app){
+ 'use strict';
+
+ // add controller to app
+ app.controller('ResultsPageController', ResultsPageController);
+
+ // controller dependencies
+ ResultsPageController.$inject = ['PropertiesService', '$window']
+
+ // controller
+ function ResultsPageController(PropertiesService, $window) {
+ var vm = this;
+
+ // properties
+ vm.queryData = {};
+ vm.loadMore = {};
+
+ // methods
+ vm.getMoreProperties = getMoreProperties;
+ vm.goToPropertyDetails = goToPropertyDetails;
+
+ // startup method
+ activate();
+ ////////////////////////
+
+ /**
+ * startup method. Get queried results and sets Load More state
+ */
+ function activate() {
+ vm.queryData = PropertiesService.getLastQueryResults();
+
+ vm.loadMore = {
+ loading: false,
+ label: 'Load more ...',
+ canLoad: vm.queryData.page < vm.queryData.pages
+ };
+ }
+
+ /**
+ * Toggle Load More state
+ */
+ function toggleLoadMore() {
+ if (vm.loadMore.loading) {
+ vm.loadMore.loading = false;
+ vm.loadMore.label = 'Load more ...';
+ } else {
+ vm.loadMore.loading = true;
+ vm.loadMore.label = 'Loading ...';
+ }
+
+ vm.loadMore.canLoad = vm.queryData.page < vm.queryData.pages;
+ }
+
+ /**
+ * Navigates to Property details page with selected property
+ *
+ * @param Object property
+ */
+ function goToPropertyDetails(property) {
+ // navi is global from onsen-navigator
+ $window.navi.pushPage('pages/property-details/property-details.html', {
+ data: {
+ selectedProperty: property
+ }
+ });
+ }
+
+ /**
+ * get more results from last query
+ */
+ function getMoreProperties() {
+ toggleLoadMore();
+
+ PropertiesService.loadMore()
+ .then(function(){
+ toggleLoadMore();
+ vm.queryData = PropertiesService.getLastQueryResults();
+ }).
+ catch(function(error){
+ toggleLoadMore();
+ alert(error.message)
+ });
+ }
+ }
+})(window.app);
\ No newline at end of file
diff --git a/onsenui2/www/pages/results/results.html b/onsenui2/www/pages/results/results.html
new file mode 100644
index 00000000..4a9d3e2f
--- /dev/null
+++ b/onsenui2/www/pages/results/results.html
@@ -0,0 +1,29 @@
+
+
+
+ Back
+
+ {{ vm.queryData.showing }} of {{ ::vm.queryData.results }} results
+
+
+
+
+
+
![]()
+
+
+ {{ ::property.price_formatted }}
+ {{ ::property.title }}
+
+
+
+
+
+
+
+ {{ vm.loadMore.label }}
+ Results for {{ ::vm.queryData.searchTerm }}, showing {{ vm.queryData.showing }} of {{ ::vm.queryData.results }} properties
+
+
+
+
diff --git a/onsenui2/www/res/.pgbomit b/onsenui2/www/res/.pgbomit
new file mode 100644
index 00000000..e69de29b
diff --git a/onsenui2/www/res/icon/android/drawable-hdpi-icon.png b/onsenui2/www/res/icon/android/drawable-hdpi-icon.png
new file mode 100644
index 00000000..0f97f196
Binary files /dev/null and b/onsenui2/www/res/icon/android/drawable-hdpi-icon.png differ
diff --git a/onsenui2/www/res/icon/android/drawable-ldpi-icon.png b/onsenui2/www/res/icon/android/drawable-ldpi-icon.png
new file mode 100644
index 00000000..5e0b217e
Binary files /dev/null and b/onsenui2/www/res/icon/android/drawable-ldpi-icon.png differ
diff --git a/onsenui2/www/res/icon/android/drawable-mdpi-icon.png b/onsenui2/www/res/icon/android/drawable-mdpi-icon.png
new file mode 100644
index 00000000..27cf9853
Binary files /dev/null and b/onsenui2/www/res/icon/android/drawable-mdpi-icon.png differ
diff --git a/onsenui2/www/res/icon/android/drawable-xhdpi-icon.png b/onsenui2/www/res/icon/android/drawable-xhdpi-icon.png
new file mode 100644
index 00000000..918c4ab9
Binary files /dev/null and b/onsenui2/www/res/icon/android/drawable-xhdpi-icon.png differ
diff --git a/onsenui2/www/res/icon/android/drawable-xxhdpi-icon.png b/onsenui2/www/res/icon/android/drawable-xxhdpi-icon.png
new file mode 100644
index 00000000..5ab972c5
Binary files /dev/null and b/onsenui2/www/res/icon/android/drawable-xxhdpi-icon.png differ
diff --git a/onsenui2/www/res/icon/android/drawable-xxxhdpi-icon.png b/onsenui2/www/res/icon/android/drawable-xxxhdpi-icon.png
new file mode 100644
index 00000000..75f7fbf5
Binary files /dev/null and b/onsenui2/www/res/icon/android/drawable-xxxhdpi-icon.png differ
diff --git a/onsenui2/www/res/icon/ios/icon-40.png b/onsenui2/www/res/icon/ios/icon-40.png
new file mode 100644
index 00000000..c772f33d
Binary files /dev/null and b/onsenui2/www/res/icon/ios/icon-40.png differ
diff --git a/onsenui2/www/res/icon/ios/icon-40@2x.png b/onsenui2/www/res/icon/ios/icon-40@2x.png
new file mode 100644
index 00000000..81e0776f
Binary files /dev/null and b/onsenui2/www/res/icon/ios/icon-40@2x.png differ
diff --git a/onsenui2/www/res/icon/ios/icon-50.png b/onsenui2/www/res/icon/ios/icon-50.png
new file mode 100644
index 00000000..fdd150c5
Binary files /dev/null and b/onsenui2/www/res/icon/ios/icon-50.png differ
diff --git a/onsenui2/www/res/icon/ios/icon-50@2x.png b/onsenui2/www/res/icon/ios/icon-50@2x.png
new file mode 100644
index 00000000..fa10febc
Binary files /dev/null and b/onsenui2/www/res/icon/ios/icon-50@2x.png differ
diff --git a/onsenui2/www/res/icon/ios/icon-60.png b/onsenui2/www/res/icon/ios/icon-60.png
new file mode 100644
index 00000000..884ee0c6
Binary files /dev/null and b/onsenui2/www/res/icon/ios/icon-60.png differ
diff --git a/onsenui2/www/res/icon/ios/icon-60@2x.png b/onsenui2/www/res/icon/ios/icon-60@2x.png
new file mode 100644
index 00000000..884ee0c6
Binary files /dev/null and b/onsenui2/www/res/icon/ios/icon-60@2x.png differ
diff --git a/onsenui2/www/res/icon/ios/icon-60@3x.png b/onsenui2/www/res/icon/ios/icon-60@3x.png
new file mode 100644
index 00000000..f8feee58
Binary files /dev/null and b/onsenui2/www/res/icon/ios/icon-60@3x.png differ
diff --git a/onsenui2/www/res/icon/ios/icon-72.png b/onsenui2/www/res/icon/ios/icon-72.png
new file mode 100644
index 00000000..0f97f196
Binary files /dev/null and b/onsenui2/www/res/icon/ios/icon-72.png differ
diff --git a/onsenui2/www/res/icon/ios/icon-72@2x.png b/onsenui2/www/res/icon/ios/icon-72@2x.png
new file mode 100644
index 00000000..5ab972c5
Binary files /dev/null and b/onsenui2/www/res/icon/ios/icon-72@2x.png differ
diff --git a/onsenui2/www/res/icon/ios/icon-76.png b/onsenui2/www/res/icon/ios/icon-76.png
new file mode 100644
index 00000000..352c0f64
Binary files /dev/null and b/onsenui2/www/res/icon/ios/icon-76.png differ
diff --git a/onsenui2/www/res/icon/ios/icon-76@2x.png b/onsenui2/www/res/icon/ios/icon-76@2x.png
new file mode 100644
index 00000000..f9c77f3d
Binary files /dev/null and b/onsenui2/www/res/icon/ios/icon-76@2x.png differ
diff --git a/onsenui2/www/res/icon/ios/icon-small.png b/onsenui2/www/res/icon/ios/icon-small.png
new file mode 100644
index 00000000..0fe3536a
Binary files /dev/null and b/onsenui2/www/res/icon/ios/icon-small.png differ
diff --git a/onsenui2/www/res/icon/ios/icon-small@2x.png b/onsenui2/www/res/icon/ios/icon-small@2x.png
new file mode 100644
index 00000000..cec63e0d
Binary files /dev/null and b/onsenui2/www/res/icon/ios/icon-small@2x.png differ
diff --git a/onsenui2/www/res/icon/ios/icon-small@3x.png b/onsenui2/www/res/icon/ios/icon-small@3x.png
new file mode 100644
index 00000000..b5ba8a72
Binary files /dev/null and b/onsenui2/www/res/icon/ios/icon-small@3x.png differ
diff --git a/onsenui2/www/res/icon/ios/icon.png b/onsenui2/www/res/icon/ios/icon.png
new file mode 100644
index 00000000..2cb89282
Binary files /dev/null and b/onsenui2/www/res/icon/ios/icon.png differ
diff --git a/onsenui2/www/res/icon/ios/icon@2x.png b/onsenui2/www/res/icon/ios/icon@2x.png
new file mode 100644
index 00000000..224a0cf0
Binary files /dev/null and b/onsenui2/www/res/icon/ios/icon@2x.png differ
diff --git a/onsenui2/www/res/screen/android/drawable-land-hdpi-screen.png b/onsenui2/www/res/screen/android/drawable-land-hdpi-screen.png
new file mode 100644
index 00000000..d1849f31
Binary files /dev/null and b/onsenui2/www/res/screen/android/drawable-land-hdpi-screen.png differ
diff --git a/onsenui2/www/res/screen/android/drawable-land-ldpi-screen.png b/onsenui2/www/res/screen/android/drawable-land-ldpi-screen.png
new file mode 100644
index 00000000..59e16da8
Binary files /dev/null and b/onsenui2/www/res/screen/android/drawable-land-ldpi-screen.png differ
diff --git a/onsenui2/www/res/screen/android/drawable-land-mdpi-screen.png b/onsenui2/www/res/screen/android/drawable-land-mdpi-screen.png
new file mode 100644
index 00000000..9b66b68c
Binary files /dev/null and b/onsenui2/www/res/screen/android/drawable-land-mdpi-screen.png differ
diff --git a/onsenui2/www/res/screen/android/drawable-land-xhdpi-screen.png b/onsenui2/www/res/screen/android/drawable-land-xhdpi-screen.png
new file mode 100644
index 00000000..7daceb7d
Binary files /dev/null and b/onsenui2/www/res/screen/android/drawable-land-xhdpi-screen.png differ
diff --git a/onsenui2/www/res/screen/android/drawable-land-xxhdpi-screen.png b/onsenui2/www/res/screen/android/drawable-land-xxhdpi-screen.png
new file mode 100644
index 00000000..0d540e8b
Binary files /dev/null and b/onsenui2/www/res/screen/android/drawable-land-xxhdpi-screen.png differ
diff --git a/onsenui2/www/res/screen/android/drawable-land-xxxhdpi-screen.png b/onsenui2/www/res/screen/android/drawable-land-xxxhdpi-screen.png
new file mode 100644
index 00000000..8f83bf21
Binary files /dev/null and b/onsenui2/www/res/screen/android/drawable-land-xxxhdpi-screen.png differ
diff --git a/onsenui2/www/res/screen/android/drawable-port-hdpi-screen.png b/onsenui2/www/res/screen/android/drawable-port-hdpi-screen.png
new file mode 100644
index 00000000..095af24c
Binary files /dev/null and b/onsenui2/www/res/screen/android/drawable-port-hdpi-screen.png differ
diff --git a/onsenui2/www/res/screen/android/drawable-port-ldpi-screen.png b/onsenui2/www/res/screen/android/drawable-port-ldpi-screen.png
new file mode 100644
index 00000000..fef955ac
Binary files /dev/null and b/onsenui2/www/res/screen/android/drawable-port-ldpi-screen.png differ
diff --git a/onsenui2/www/res/screen/android/drawable-port-mdpi-screen.png b/onsenui2/www/res/screen/android/drawable-port-mdpi-screen.png
new file mode 100644
index 00000000..29cfe72c
Binary files /dev/null and b/onsenui2/www/res/screen/android/drawable-port-mdpi-screen.png differ
diff --git a/onsenui2/www/res/screen/android/drawable-port-xhdpi-screen.png b/onsenui2/www/res/screen/android/drawable-port-xhdpi-screen.png
new file mode 100644
index 00000000..83e1d234
Binary files /dev/null and b/onsenui2/www/res/screen/android/drawable-port-xhdpi-screen.png differ
diff --git a/onsenui2/www/res/screen/android/drawable-port-xxhdpi-screen.png b/onsenui2/www/res/screen/android/drawable-port-xxhdpi-screen.png
new file mode 100644
index 00000000..f0251fc5
Binary files /dev/null and b/onsenui2/www/res/screen/android/drawable-port-xxhdpi-screen.png differ
diff --git a/onsenui2/www/res/screen/android/drawable-port-xxxhdpi-screen.png b/onsenui2/www/res/screen/android/drawable-port-xxxhdpi-screen.png
new file mode 100644
index 00000000..cb2fc340
Binary files /dev/null and b/onsenui2/www/res/screen/android/drawable-port-xxxhdpi-screen.png differ
diff --git a/onsenui2/www/res/screen/ios/Default-568h@2x~iphone.png b/onsenui2/www/res/screen/ios/Default-568h@2x~iphone.png
new file mode 100644
index 00000000..3b05e95e
Binary files /dev/null and b/onsenui2/www/res/screen/ios/Default-568h@2x~iphone.png differ
diff --git a/onsenui2/www/res/screen/ios/Default-667h.png b/onsenui2/www/res/screen/ios/Default-667h.png
new file mode 100644
index 00000000..7b30b52e
Binary files /dev/null and b/onsenui2/www/res/screen/ios/Default-667h.png differ
diff --git a/onsenui2/www/res/screen/ios/Default-736h.png b/onsenui2/www/res/screen/ios/Default-736h.png
new file mode 100644
index 00000000..8468d689
Binary files /dev/null and b/onsenui2/www/res/screen/ios/Default-736h.png differ
diff --git a/onsenui2/www/res/screen/ios/Default-Landscape-736h.png b/onsenui2/www/res/screen/ios/Default-Landscape-736h.png
new file mode 100644
index 00000000..5d7c7aa4
Binary files /dev/null and b/onsenui2/www/res/screen/ios/Default-Landscape-736h.png differ
diff --git a/onsenui2/www/res/screen/ios/Default-Landscape@2x~ipad.png b/onsenui2/www/res/screen/ios/Default-Landscape@2x~ipad.png
new file mode 100644
index 00000000..a4528764
Binary files /dev/null and b/onsenui2/www/res/screen/ios/Default-Landscape@2x~ipad.png differ
diff --git a/onsenui2/www/res/screen/ios/Default-Landscape~ipad.png b/onsenui2/www/res/screen/ios/Default-Landscape~ipad.png
new file mode 100644
index 00000000..652b5652
Binary files /dev/null and b/onsenui2/www/res/screen/ios/Default-Landscape~ipad.png differ
diff --git a/onsenui2/www/res/screen/ios/Default-Portrait@2x~ipad.png b/onsenui2/www/res/screen/ios/Default-Portrait@2x~ipad.png
new file mode 100644
index 00000000..6be05a54
Binary files /dev/null and b/onsenui2/www/res/screen/ios/Default-Portrait@2x~ipad.png differ
diff --git a/onsenui2/www/res/screen/ios/Default-Portrait~ipad.png b/onsenui2/www/res/screen/ios/Default-Portrait~ipad.png
new file mode 100644
index 00000000..0d0cd48c
Binary files /dev/null and b/onsenui2/www/res/screen/ios/Default-Portrait~ipad.png differ
diff --git a/onsenui2/www/res/screen/ios/Default@2x~iphone.png b/onsenui2/www/res/screen/ios/Default@2x~iphone.png
new file mode 100644
index 00000000..47af72ce
Binary files /dev/null and b/onsenui2/www/res/screen/ios/Default@2x~iphone.png differ
diff --git a/onsenui2/www/res/screen/ios/Default~iphone.png b/onsenui2/www/res/screen/ios/Default~iphone.png
new file mode 100644
index 00000000..29cfe72c
Binary files /dev/null and b/onsenui2/www/res/screen/ios/Default~iphone.png differ
diff --git a/onsenui2/www/services/favourites.service.js b/onsenui2/www/services/favourites.service.js
new file mode 100644
index 00000000..b04751df
--- /dev/null
+++ b/onsenui2/www/services/favourites.service.js
@@ -0,0 +1,142 @@
+/**
+ * Service for storing, retrieve and delete favourites properties
+ */
+(function(app){
+ 'use strict';
+
+ // add service to app
+ app.factory('FavouritesService', FavouritesService);
+
+ // dependencies of service
+ FavouritesService.$inject = ['$log', '$q', '_', 'localforage', '$window']
+
+ // service
+ function FavouritesService($log, $q, _, localforage, $window) {
+
+ // local copy of favourites
+ var favourites = null;
+
+ // public interface
+ var service = {
+ getAll: getFavourites,
+ store: storeFav,
+ remove: removeFav,
+ isFav: isFav
+ };
+
+ return service;
+
+ //////////////////////////////
+
+ /**
+ * retrieve stored favourites and cache them in service local variable
+ *
+ * @return Promise
+ */
+ function init() {
+ return localforage.getItem('favouriteProperties')
+ .then(initComplete)
+ .catch(initFailed);
+
+ function initComplete(value) {
+ favourites = value;
+ return favourites;
+ }
+
+ function initFailed(error) {
+ $log.error(error);
+ }
+ }
+
+ /**
+ * Get favourites from local variable or storage
+ *
+ * @return Promise
+ */
+ function getFavourites() {
+ if (favourites !== null) {
+ return $q.resolve(favourites);
+ } else {
+ return init();
+ }
+ }
+
+ /**
+ * Generates key from property
+ *
+ * @param Object property
+ * @return String Generated key
+ */
+ function getPropertyKey(property) {
+ return $window.btoa(property.lister_url);
+ }
+
+ /**
+ * Check if a property is favourite
+ *
+ * @param Object property
+ * @return Promise
+ */
+ function isFav(property) {
+ return getFavourites().then(isFavComplete)
+ .catch(isFavFailed);
+
+ function isFavComplete(favourites) {
+ return _.findIndex(favourites, ['key', getPropertyKey(property)]) > -1;
+ }
+
+ function isFavFailed(error) {
+ $log.error(error);
+ return false;
+ }
+ }
+
+ /**
+ * Stores a property in favourites list
+ *
+ * @param Object property
+ */
+ function storeFav(property) {
+
+ property.key = getPropertyKey(property);
+
+ if (favourites === null || _.isEmpty(favourites)) {
+ favourites = [property];
+ }
+ else {
+ // try to find term
+ var index = _.findIndex(favourites, ['key', property.key]);
+
+ // update date or insert
+ if (index > -1) {
+ favourites[index] = property;
+ }
+ else {
+ favourites.push(property);
+ }
+ }
+
+ // update storage
+ localforage.setItem('favouriteProperties', favourites);
+ }
+
+ /**
+ * Remove a property from favourites storage
+ *
+ * @param Object property
+ */
+ function removeFav(property) {
+
+ // try to find property
+ var index = _.findIndex(favourites, ['key', getPropertyKey(property)]);
+
+ // delete element from favourites
+ if (index > -1) {
+ _.pull(favourites, favourites[index]);
+ }
+
+ // update storage
+ localforage.setItem('favouriteProperties', favourites);
+ }
+ }
+})(window.app);
\ No newline at end of file
diff --git a/onsenui2/www/services/localforage.service.js b/onsenui2/www/services/localforage.service.js
new file mode 100644
index 00000000..495bcae2
--- /dev/null
+++ b/onsenui2/www/services/localforage.service.js
@@ -0,0 +1,13 @@
+(function(app){
+ 'use strict';
+
+ // Register factory
+ app.factory('localforage', LocalForageFactory);
+
+ LocalForageFactory.$inject = ['$window'];
+
+ function LocalForageFactory($window) {
+ return $window.localforage;
+ }
+
+})(window.app);
\ No newline at end of file
diff --git a/onsenui2/www/services/lodash.service.js b/onsenui2/www/services/lodash.service.js
new file mode 100644
index 00000000..0594a580
--- /dev/null
+++ b/onsenui2/www/services/lodash.service.js
@@ -0,0 +1,13 @@
+(function(app){
+ 'use strict';
+
+ // Register factory
+ app.factory('_', LodashFactory);
+
+ LodashFactory.$inject = ['$window'];
+
+ function LodashFactory($window) {
+ return $window._;
+ }
+
+})(window.app);
\ No newline at end of file
diff --git a/onsenui2/www/services/properties.service.js b/onsenui2/www/services/properties.service.js
new file mode 100644
index 00000000..ec759429
--- /dev/null
+++ b/onsenui2/www/services/properties.service.js
@@ -0,0 +1,244 @@
+/**
+ * Service for accesing Property API
+ */
+(function(app){
+ 'use strict';
+
+ // add service to app
+ app.factory('PropertiesService', PropertiesService);
+
+ // dependencies of service
+ PropertiesService.$inject = ['$log', '$http', '$window', '$q', 'PROPERTY_API']
+
+ // service
+ function PropertiesService($log, $http, $window, $q, PROPERTY_API) {
+ var lastQueryResults = {};
+ var searchTerm = '';
+ var searchCoordinates = null;
+
+ // public
+ var service = {
+ searchByTerm: searchByTerm,
+ searchByPosition: searchByPosition,
+ loadMore: loadMore,
+ getLastQueryResults: getLastQueryResults
+ };
+
+ return service;
+ //////////////////////////////
+
+ /**
+ * Compose url for term based searches
+ *
+ * @param string term Term to search
+ * @param int page Page number
+ * @return string URL
+ */
+ function composeTermUrl(term, page) {
+ return PROPERTY_API.baseUrl + '&page=' + page + '&place_name=' + term;
+ }
+
+ /**
+ * Compose url for location based searches
+ *
+ * @param Object coordinates Geolocation object
+ * @param int page Page number
+ * @return string URL
+ */
+ function composePositionUrl(coordinates, page) {
+ return PROPERTY_API.baseUrl + '&page=' + page +
+ '¢re_point=' + coordinates.lat + ',' + coordinates.lng;
+ }
+
+ /**
+ * Perform a search of properties by term
+ *
+ * @param string term
+ * @return Promise
+ */
+ function searchByTerm(term) {
+ searchCoordinates = null;
+ searchTerm = term;
+
+ return $http.get(composeTermUrl(term, 1), {timeout: 5000})
+ .then(searchByTermComplete)
+ .catch(searchByTermFailed);
+
+ function searchByTermComplete(response) {
+ return processResponse(response.data);
+ }
+
+ function searchByTermFailed(error) {
+ return processFailedResponse(error, PROPERTY_API.error.messages.GENERIC_ERROR);
+ }
+ }
+
+ /**
+ * Perform search by Position
+ *
+ * @return Promise
+ */
+ function searchByPosition() {
+ searchCoordinates = null;
+ searchTerm = '';
+
+ return $q(function(resolve, reject){
+
+ // try to get current position
+ $window.navigator.geolocation
+ .getCurrentPosition(positionSuccess, positionError, { enableHighAccuracy: true, timeout: 5000 });
+
+ function positionSuccess(position) {
+ searchCoordinates = {
+ lat: position.coords.latitude,
+ lng: position.coords.longitude
+ };
+
+ $http.get(composePositionUrl(searchCoordinates, 1), {timeout: 5000})
+ .then(searchByPositionComplete)
+ .catch(positionError);
+
+ function searchByPositionComplete(response) {
+ var info = processResponse(response.data);
+ resolve(info);
+ }
+ }
+
+ function positionError(error) {
+ var msg = '';
+
+ if (error.code === error.PERMISSION_DENIED) {
+ msg = PROPERTY_API.error.messages.LOCATION_DISABLED;
+ } else if (error.code === error.POSITION_UNAVAILABLE) {
+ msg = PROPERTY_API.error.messages.LOCATION_UNAVAILABLE;
+ }
+ else {
+ msg = PROPERTY_API.error.messages.GENERIC_ERROR
+ }
+
+ var err = processFailedResponse(error, msg);
+ reject(err);
+ }
+ });
+ }
+
+ /**
+ * Process successfull response from API
+ *
+ * @param Object data API response
+ * @return Object Procesed results
+ */
+ function processResponse(data) {
+
+ switch (data.response.application_response_code) {
+ // OK codes
+ case '100':
+ case '101':
+ case '110':
+ // Some properties found
+ if (data.response.listings.length) {
+ lastQueryResults = {
+ status: PROPERTY_API.status.SUCCESS,
+ message: '',
+ searchTerm: searchTerm,
+ searchCoordinates: searchCoordinates,
+ page: data.response.page,
+ pages: data.response.total_pages + 1,
+ showing: data.response.listings.length,
+ results: data.response.total_results,
+ properties: data.response.listings
+ };
+ }
+ // No properties found
+ else {
+ lastQueryResults = {
+ status: PROPERTY_API.status.ERROR,
+ message: 'There were no properties found for the given location.',
+ searchTerm: searchTerm,
+ searchCoordinates: searchCoordinates
+ };
+ }
+
+ break;
+ // Ambiguous codes
+ case '200':
+ case '202':
+ lastQueryResults = {
+ status: PROPERTY_API.status.AMBIGUOUS,
+ message: '',
+ searchCoordinates: searchCoordinates,
+ searchTerm: searchTerm,
+ locations: data.response.locations
+ };
+ break;
+ // everything else is considered as error
+ default:
+ lastQueryResults = processFailedResponse(null, PROPERTY_API.error.messages.GENERIC_ERROR);
+ } // switch
+
+ return lastQueryResults;
+ }
+
+ /**
+ * Process erroneous responses from API
+ *
+ * @param string error Browser description of error
+ * @param string msg Message shown to user
+ * @return Object
+ */
+ function processFailedResponse(error, msg) {
+ $log.error(error);
+
+ lastQueryResults = {
+ status: PROPERTY_API.status.ERROR, // error
+ message: msg,
+ searchCoordinates: searchCoordinates,
+ searchTerm: searchTerm
+ };
+
+ return lastQueryResults;
+ }
+
+ /**
+ * Use last query parameter to get more results by asking for next page.
+ *
+ * @return Promise
+ */
+ function loadMore() {
+
+ // Term based search
+ if (lastQueryResults.searchCoordinates === null) {
+ return $http.get(composeTermUrl(lastQueryResults.searchTerm, lastQueryResults.page +1))
+ .then(loadMoreComplete)
+ .catch(loadMoreFailed);
+ }
+ // Location based search
+ else {
+ return $http.get(composePositionUrl(lastQueryResults.searchCoordinates, lastQueryResults.page +1))
+ .then(loadMoreComplete)
+ .catch(loadMoreFailed);
+ }
+
+ function loadMoreComplete(response) {
+ lastQueryResults.page++;
+ lastQueryResults.properties = lastQueryResults.properties.concat(response.data.response.listings);
+ lastQueryResults.showing = lastQueryResults.properties.length;
+
+ return response.data;
+ }
+
+ function loadMoreFailed(error) {
+ return processFailedResponse(error, PROPERTY_API.error.messages.GENERIC_ERROR);
+ }
+ }
+
+ /**
+ * Return last results obtained
+ *
+ * @return Object
+ */
+ function getLastQueryResults() {
+ return lastQueryResults;
+ }
+ }
+})(window.app);
\ No newline at end of file
diff --git a/onsenui2/www/services/recent-searches.service.js b/onsenui2/www/services/recent-searches.service.js
new file mode 100644
index 00000000..d27ad117
--- /dev/null
+++ b/onsenui2/www/services/recent-searches.service.js
@@ -0,0 +1,100 @@
+/**
+ * Service for storing and retrieving recent searches
+ */
+(function(app){
+ 'use strict';
+
+ // add service to app
+ app.factory('RecentSearchesService', RecentSearchesService);
+
+ // dependencies of service
+ RecentSearchesService.$inject = ['$log', '$q', '_', 'localforage', '$timeout']
+
+ // service
+ function RecentSearchesService($log, $q, _, localforage) {
+ // local copy of storage
+ var searches = null;
+
+ // public
+ var service = {
+ get: getRecentSearches,
+ store: storeSearch
+ };
+
+ return service;
+
+ //////////////////////////////
+
+ /**
+ * retrieve stored recent searches and cache them in service local variable
+ *
+ * @return Promise
+ */
+ function init() {
+ return localforage.getItem('recentSearches')
+ .then(initComplete)
+ .catch(initFailed);
+
+ function initComplete(value) {
+ searches = value;
+ return searches;
+ }
+
+ function initFailed(error) {
+ $log.error(error);
+ }
+ }
+
+ /**
+ * Get recent searches
+ *
+ * @return Promise
+ */
+ function getRecentSearches() {
+ if (searches !== null) {
+ return $q.resolve(searches);
+ } else {
+ return init();
+ }
+ }
+
+ /**
+ * Store recent search
+ *
+ * @param Object searchInfo Search to be stored
+ */
+ function storeSearch(searchInfo) {
+ var search = {
+ term: searchInfo.term,
+ results: searchInfo.results,
+ date: new Date().toISOString()
+ }
+
+ // no searches stored
+ if (searches === null) {
+ searches = [search];
+ }
+ else {
+ // try to find term
+ var index = _.findIndex(searches, ['term', search.term]);
+
+ // update date or insert
+ if (index > -1) {
+ searches[index] = search;
+ }
+ else {
+ searches.push(search);
+ }
+
+ // order
+ searches = _.reverse(_.sortBy(searches, ['date']));
+
+ // only 6 elements
+ searches = _.slice(searches, 0, 6);
+ }
+
+ // persist storage
+ localforage.setItem('recentSearches', searches);
+ }
+ }
+})(window.app);
\ No newline at end of file