diff options
author | Yongkook Kim <yk22.kim@samsung.com> | 2015-12-09 15:52:14 +0900 |
---|---|---|
committer | Yongkook Kim <yk22.kim@samsung.com> | 2015-12-09 15:52:53 +0900 |
commit | e1d475bac2aea0dbe037607cf7445eb145d85019 (patch) | |
tree | d13e3fc4ce54403eab52e8fb518cdce47fc4bbd1 /js | |
parent | 721ac8120af82d883e00ab499158d8b95a35bcf4 (diff) | |
download | altimeter-master.tar.gz altimeter-master.tar.bz2 altimeter-master.zip |
Signed-off-by: Yongkook Kim <yk22.kim@samsung.com>
Change-Id: I2873b2234e00d678d387dcec1b3127d90e1aa8f4
Diffstat (limited to 'js')
-rw-r--r-- | js/app.js | 47 | ||||
-rw-r--r-- | js/core/.gitignore | 2 | ||||
-rw-r--r-- | js/core/.jscsrc | 44 | ||||
-rw-r--r-- | js/core/.jshintignore | 2 | ||||
-rw-r--r-- | js/core/.jshintrc | 25 | ||||
-rw-r--r-- | js/core/README.md | 53 | ||||
-rw-r--r-- | js/core/core.js | 672 | ||||
-rw-r--r-- | js/core/core/application.js | 221 | ||||
-rw-r--r-- | js/core/core/event.js | 323 | ||||
-rw-r--r-- | js/core/core/storage/idb.js | 320 | ||||
-rw-r--r-- | js/core/core/systeminfo.js | 120 | ||||
-rw-r--r-- | js/core/core/tizen.js | 44 | ||||
-rw-r--r-- | js/core/core/window.js | 43 | ||||
-rw-r--r-- | js/helpers/dom.js | 53 | ||||
-rw-r--r-- | js/models/pressure.js | 223 | ||||
-rw-r--r-- | js/models/settings.js | 153 | ||||
-rw-r--r-- | js/tau-config.js | 24 | ||||
-rw-r--r-- | js/views/init.js | 102 | ||||
-rw-r--r-- | js/views/main.js | 355 |
19 files changed, 2826 insertions, 0 deletions
diff --git a/js/app.js b/js/app.js new file mode 100644 index 0000000..c9b0e4c --- /dev/null +++ b/js/app.js @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*global require, define, console, $*/ + +/** + * App module. + * @requires {@link Altimeter/views/init} + * @namespace Altimeter/app + * @memberof Altimeter + */ + +define({ + name: 'app', + requires: [ + 'views/init' + ], + def: function appInit() { + 'use strict'; + + /** + * Initializes the app. + * @memberof Altimeter/app + */ + function init() { + console.log('APP: init()'); + } + + return { + init: init + }; + } +}); + diff --git a/js/core/.gitignore b/js/core/.gitignore new file mode 100644 index 0000000..af95e6b --- /dev/null +++ b/js/core/.gitignore @@ -0,0 +1,2 @@ +/tests/_build +/tests/node_modules
\ No newline at end of file diff --git a/js/core/.jscsrc b/js/core/.jscsrc new file mode 100644 index 0000000..c51de4b --- /dev/null +++ b/js/core/.jscsrc @@ -0,0 +1,44 @@ +{ + "excludeFiles": [ + "*/node_modules/*", + "*/libs/*", + "*/lib/*" + ], + + "validateJSDoc": { + "checkParamNames": true, + "checkRedundantParams": true, + "requireParamTypes": true + }, + + "maximumLineLength": 80, + + + "requireCurlyBraces": ["if", "else", "for", "while", "do", "try", "catch"], + "requireMultipleVarDecl": true, + "requireBlocksOnNewline": true, + "requireLineFeedAtFileEnd": true, + "requireDotNotation": true, + "requireBlocksOnNewline": 1, + "requireSpaceAfterLineComment": true, + "requireSpaceBeforeBlockStatements": true, + "requireSpacesInConditionalExpression": true, + "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"], + "requireSpacesInFunctionExpression": { + "beforeOpeningCurlyBrace": true + }, + "requireSpaceBeforeBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="], + "requireSpaceAfterBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="], + + "disallowAnonymousFunctions": true, + "disallowKeywords": ["with"], + "disallowEmptyBlocks": true, + + "disallowNewlineBeforeBlockStatements": true, + "disallowSpaceAfterObjectKeys": true, + "disallowSpacesInsideArrayBrackets": true, + "disallowSpacesInsideParentheses": true, + "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"], + "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"], + "disallowSpaceBeforeBinaryOperators": [","] +} diff --git a/js/core/.jshintignore b/js/core/.jshintignore new file mode 100644 index 0000000..039e6b2 --- /dev/null +++ b/js/core/.jshintignore @@ -0,0 +1,2 @@ +*/node_modules/* +*/libs/* diff --git a/js/core/.jshintrc b/js/core/.jshintrc new file mode 100644 index 0000000..115de8b --- /dev/null +++ b/js/core/.jshintrc @@ -0,0 +1,25 @@ +{ + "browser": false, + "curly": true, + "devel": false, + "globals": {}, + "immed": true, + "indent": 4, + "latedef": true, + "maxcomplexity": 7, + "maxdepth": 3, + "maxlen": 80, + "maxparams": 5, + "maxstatements": 30, + "noempty": true, + "nomen": true, + "nonew": true, + "onevar": true, + "plusplus": true, + "quotmark": "single", + "strict": true, + "trailing": true, + "undef": true, + "unused": "strict", + "white": true +} diff --git a/js/core/README.md b/js/core/README.md new file mode 100644 index 0000000..faef037 --- /dev/null +++ b/js/core/README.md @@ -0,0 +1,53 @@ + +# Overview # + +Reference Web Applications Core uses a simple MVP (Model View Presenter) architecture. + +There are a core part which determines the architecture and an app part which determines the application behavior. + + +# Overview of core.js # + +`core.js` implements simple AMD (Asynchronous Module Definition) and specifies module defining. + +Modules definition organizes code into simple units (modules). +Module can refer to other modules – dependency references. + + +## Loading ## + +`core.js` loads files with a different approach than <script> tags in HTML file. + +`core.js` loads each file as a script tag, using _document.createElement_ and _head.appendChild_ and then waits for all dependencies to load, figures the right order to call definitions of module. + +## Usage ## + +Adding `core.js` to index.html: +``` +{@lang xml}<script src="./js/libs/core/core.js" data-main="./js/app.js"></script> +``` + + +Where `app.js` is the main application module. + +``` +{@lang javascript}define({ + name: 'app', + def: function def() {} +}); +``` + +### Defining a module ### +A module is a file with simple code unit, different from a traditional script file. Module avoids impact on global namespace like _window_. +Any valid return from a module is allowed, module can return objects, functions or nothing. If module definition return object with +_init_ method then module will be automatically initialized. +There should only be __one__ module definition per file. + +[See examples how to define a module](global.html#define) + + +# Contributors # + +* [Sergiusz Struminski](mailto:s.struminski@samsung.com) +* [Pawel Sierszen](mailto:p.sierszen@samsung.com) +* [Kamil Stepczuk](mailto:k.stepczuk@samsung.com) diff --git a/js/core/core.js b/js/core/core.js new file mode 100644 index 0000000..9a25ede --- /dev/null +++ b/js/core/core.js @@ -0,0 +1,672 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * @namespace core + * @author Sergiusz Struminski <s.struminski@samsung.com> + * @author Pawel Sierszen <p.sierszen@samsung.com> + */ +(function core(global) { + 'use strict'; + + /** + * Public object. + * @type {object} + */ + var publicAPI = {}, + + /** + * Document element. + * @type {object} + */ + document = global.document, + + /** + * Head element. + * @type {HTMLHeadElement} + */ + head = document.getElementsByTagName('head')[0], + + /** + * Internal object cache + * @type {object} + */ + modules = {}, + + /** + * Internal config + * @type {object} + */ + cfg = { + + /** + * Default path to modules. + * @type {string} + */ + defaultPath: './js/', + + /** + * Path to core modules. + * @type {string} + */ + basePath: null, + + /** + * Path to application modules. + * @type {string} + */ + modulePath: null + }; + + /** + * Generic Module class. + * @private + */ + function Module(name) { + // Module name. + this.name = name; + return; + } + + /** + * Returns correct path for modules. + * @private + * @param {string} data Current path. + * @return {string} New path. + */ + function getPath(data) { + var index = data.lastIndexOf('/'), + path = data.substr(0, index + 1); + + return path || './'; + } + + /** + * Have all requires been sorted already? + * @private + * @param {string[]} requires Requires. + * @param {string[]} sorted Sorted requires. + * @return {boolean} result. + */ + function areSorted(requires, sorted) { + var i = 0, + depsLen = requires.length, + result = true; + for (i = 0; i < depsLen; i += 1) { + // Has mod been sorted already? + result = result && (sorted.indexOf(requires[i]) !== -1); + } + return result; + } + + /** + * Sort modules by requires (dependents last), + * returning sorted list of module names. + * @private + * @param {object} modules Modules. + */ + function sort(modules) { + + var name = null, + // Modules to be sorted. + pending = [], + // Modules already sorted. + sorted = [], + // Remember length of pending list for each module. + visited = {}, + currModule = null; + + for (name in modules) { + if (modules.hasOwnProperty(name)) { + if (modules[name].instance) { + // Already linked. + sorted.push(name); + } else { + // Sort for linking. + pending.push(name); + } + } + } + + // Repeat while there are modules pending. + while (pending.length > 0) { + + // Consider the next pending module + currModule = pending.shift(); + + // If we've been here and have not made any progress, we are looping + // (no support for cyclic module requires). + if (visited[currModule] && visited[currModule] <= pending.length) { + throw new Error('No support for circular module dependency.'); + } + visited[currModule] = pending.length; + + // Consider the current module's import requires. + if (areSorted(modules[currModule].requires, sorted)) { + // Requires done, module done. + sorted.push(currModule); + } else { + // Some requires still pending. + pending.push(currModule); + } + } + + return sorted; + } + + /** + * Merge the contents of two objects into the first object. + * @private + * @param {Object} target Target object (child). + * @param {Object} source Source object (parent). + * @return {Object} Target object. + */ + function extend(target, source) { + var prop = null; + for (prop in source) { + if (source.hasOwnProperty(prop)) { + Object.defineProperty( + target, + prop, + { + value: source[prop] + } + ); + } + } + return target; + } + + /** + * Create the object using Def as a constructor. + * In this case the object inherits the prototype from Def. + * @private + * @param {function} Def Constructing function. + * @param {object[]} args Parameters for the constructing function. + * @return {object} Constructed object. + */ + function construct(Def, args) { + var argsLen = args.length; + + // Switch/case is used for performance reasons. + switch (argsLen) { + case 0: + return new Def(); + case 1: + return new Def(args[0]); + case 2: + return new Def(args[0], args[1]); + case 3: + return new Def(args[0], args[1], args[2]); + case 4: + return new Def(args[0], args[1], args[2], args[3]); + case 5: + return new Def(args[0], args[1], args[2], args[3], args[4]); + default: + // Too many parameters, use a short form instead + return Def.apply(Object.create(Def.prototype), args); + } + } + + /** + * Creates an object using the passed constructor and parameters. + * @private + * @param {function} Def Constructing function. + * @param {object[]} args Parameters for the constructing function. + * @return {object} Object of Def type. + */ + function instantiate(Def, args) { + var obj = null, proto = null; + + obj = construct(Def, args); + + // Constructors don't have to return anything, but we need at least + // an empty object. + if (!obj) { + obj = {}; + } + + /** + * If the module returns a plain object, we need to fix this. + * Create an object with a valid prototype + * and extend it by copying properties from the original object. + * The previous prototype, if any, is ignored. + * Only modules created with Object function will be extended. + * It is for ignore global objects like "window" or "tizen". + */ + proto = Object.getPrototypeOf(obj); + if (proto !== null && !Object.prototype.isPrototypeOf(proto)) { + obj = extend( + Object.create(Def.prototype), + obj + ); + } + + return obj; + } + + /** + * Assigns nested attributes. + * @private + * @param {object} obj Object. + * @param {string[]} pathElements Elements array. + * @param {object} value Object. + */ + function assignNested(obj, pathElements, value) { + var i, key = pathElements.pop(); + // Check the path. + for (i = 0; i < pathElements.length; i += 1) { + // If empty create an empty object here. + obj = obj[pathElements[i]] = obj[pathElements[i]] || {}; + } + obj[key] = value; + } + + /** + * Returns required module instance. + * Parameters are passed to the constructor. + * @private + * @param {string} moduleName Module name. + * @param {object} reqModule Required module object. + * @return {object} Module instance. + */ + function requireInstance(moduleName, reqModule) { + var instance = reqModule.instance; + if (reqModule.name === 'core/event') { + // Make new object inherited from core/event module + // for adding additional properties (per caller module). + instance = Object.create(reqModule.instance); + // Module name used to fire events. + instance.evName = moduleName.replace(/\//g, '.'); + } + return instance; + } + + /** + * Creates parameters (from required modules). + * Parameteres are passed to the constructor. + * @private + * @param {object} module Module object. + * @return {object[]} params. + */ + function createParams(module) { + var def = module.def, + requires = module.requires, + params = [], + req = {}, + instance = null, + i = 0; + + if (def.length === 1 && requires.length > 1) { + // Collect requires as object. + for (i = requires.length - 1; i >= 0; i -= 1) { + instance = requireInstance(module.name, modules[requires[i]]); + + // Full name keys for array-like indexing. + req[requires[i]] = instance; + + // Nested objects for cleaner syntax. + assignNested(req, requires[i].split('/'), instance); + } + params.push(req); + + } else if (def.length === requires.length) { + // Collect requires as modules. + for (i = requires.length - 1; i >= 0; i -= 1) { + params[i] = requireInstance(module.name, modules[requires[i]]); + } + + } else if (def.length !== 0) { + // Invalid number of params. + // Definition module params length is greater than zero + // and different than requires params length. + throw new Error( + 'Invalid number of params in ' + def.name + + '- expected ' + requires.length + ' but is ' + def.length + ); + } + + return params; + } + + + /** + * Links and runs modules in the order in which they were loaded. + * @private + */ + function link() { + var i = 0, + sorted = [], + sortedLen = 0, + name = '', + module = null; + + // Sort modules in requires order. + sorted = sort(modules); + sortedLen = sorted.length; + + // Create instances of modules in requires order. + for (i = 0; i < sortedLen; i += 1) { + name = sorted[i]; + module = modules[name]; + + if (module.instance === undefined) { + module.initialized = false; + + // Each module should inherit from a generic Module object. + module.def.prototype = new Module(name); + + // Execute module code, pass requires, record exports. + modules[name].instance = instantiate( + module.def, + createParams(module) + ); + } + } + + // Initialize modules in requires order. + // It must be in different loop (see above) + // because we need every instance ready. + for (i = 0; i < sortedLen; i += 1) { + name = sorted[i]; + module = modules[name]; + + if (module.instance !== undefined && !module.initialized) { + if (typeof modules[name].instance.init === 'function') { + modules[name].instance.init(); + module.initialized = true; + } + } + } + + } + + /** + * Returns instance of module. + * @global + * + * @example + * // Define `foo` module which require `bar` module: + * define({ + * name: 'foo', + * requires: ['bar'], + * def: function def(bar) {} + * }); + * + * // Define `bar` module which needs some `foo` functionalities: + * // You can't define a circular dependency + * // (foo needs bar and bar needs foo) + * define({ + * name: 'bar', + * requires: ['foo'], + * def: function def(foo) {} + * }); + * + * // In that case use: + * define({ + * name: 'bar', + * def: function def() { + * var foo; + * function init() { + * foo = require('foo'); + * } + * return { + * init: init + * } + * } + * }); + * + * @throws {Error} Module must be defined. + * @throws {Error} Module must be an instance. + * + * @param {string} moduleName Module name. + * @return {object} Module instance. + */ + function require(moduleName) { + var module = modules[moduleName]; + + if (module === undefined) { + throw new Error('Module ' + moduleName + ' must be defined.'); + } + + if (module.instance === undefined) { + throw new Error('The instance of ' + moduleName + + ' doesn\'t exist yet.'); + } + + return module.instance; + } + + /** + * Loads a script. + * @private + * @param {string} src Script src. + */ + function loadScript(src) { + var script = null; + + script = document.createElement('script'); + script.setAttribute('src', src); + script.addEventListener('error', function error() { + throw new Error( + 'Failed to load "' + src + '" script' + ); + }); + head.appendChild(script); + } + + /** + * Loads a module. + * @private + * @param {string} moduleName Module name. + */ + function load(moduleName) { + var modulePath = ''; + + if (modules[moduleName] !== undefined) { + return false; + } + + modules[moduleName] = {}; + + if (moduleName.indexOf('core') === 0) { + modulePath = cfg.basePath || cfg.defaultPath; + } else { + modulePath = cfg.modulePath || cfg.defaultPath; + } + + loadScript(modulePath + moduleName + '.js'); + + return true; + } + + /** + * Check whether this was the last module to be loaded + * in a given dependency group. + * If yes, start linking and running modules. + * @private + */ + function loaded() { + var m = null, + pending = []; + + for (m in modules) { + if (modules.hasOwnProperty(m) && modules[m].name === undefined) { + pending.push(m); + } + } + + if (pending.length === 0) { + link(); + } + } + + /** + * The function that handles definitions of modules. + * @global + * + * @example + * // Define `foo` module: + * define({ + * name: 'foo', + * def: function def() {} + * }); + * + * @example + * // Define `bar` module: + * define({ + * name: 'bar', + * def: function def() {} + * }); + * + * @example + * // Define `foo` module which require `bar` module: + * define({ + * name: 'foo', + * requires: ['bar'], + * def: function def(bar) {} + * }); + * + * @example + * // Define `foo` module which require `bar1` and `bar2` module: + * define({ + * name: 'foo', + * requires: ['bar1', 'bar2'], + * def: function def(bar1, bar2) {} + * }); + * + * @example + * // Define `foo` module which require `bar1` and `bar2` module: + * define({ + * name: 'foo', + * requires: ['bar1', 'bar2'], + * def: function def(require) { + * var bar1 = require.bar1, + * bar2 = require.bar2; + * } + * }); + * + * @example + * // Define `foo` module which require `path/bar1` and `path/bar2` module: + * define({ + * name: 'foo', + * requires: ['path/bar1', 'path/bar2'], + * def: function def(require) { + * // recommended + * var bar1 = require.path.bar1, + * bar2 = require.path.bar2; + * // or + * var bar1 = require['path/bar1'], + * bar2 = require['path/bar2']; + * } + * }); + * + * @example + * // Define `foo` module which is automatically initialized + * // during definition: + * define({ + * name: 'foo', + * def: function def() { + * // module definition + * function init() { + * // init action + * } + * + * // return the module value with init function + * return { + * init: init + * }; + * } + * }); + * + * @throws {Error} Module must have name and definititon. + * @throws {Error} Module is already defined. + * + * @param {object} module Module object. + * @param {string} module.name Module name. + * @param {string[]} [module.requires] Module requires. + * @param {function} module.def Module definititon. + */ + function define(module) { + var i = 0, + j = 0; + + module = module || {}; + + if (module.name === undefined || module.def === undefined) { + throw new Error( + 'Module must have name and definition' + ); + } + + if (modules[module.name] !== undefined && + modules[module.name].name !== undefined) { + throw new Error( + 'Module "' + module.name + '" is already defined' + ); + } + + module.requires = module.requires || []; + modules[module.name] = module; + + // Load required modules. + for (i = 0, j = module.requires.length; i < j; i += 1) { + load(module.requires[i]); + } + + // Check for loaded modules. + loaded(); + + return true; + } + + /** + * Looks for a data-main attribute in script elements. + * Data-main attribute tells core to load main application script. + * @private + * @return {boolean} + */ + function main() { + var i = 0, + len = 0, + scripts = document.getElementsByTagName('script'), + script = null, + dataMain = null; + + for (i = 0, len = scripts.length; i < len; i += 1) { + script = scripts[i]; + dataMain = script.getAttribute('data-main'); + if (dataMain) { + cfg.modulePath = getPath(dataMain); + cfg.basePath = getPath(script.getAttribute('src')); + loadScript(dataMain); + return true; + } + } + return true; + } + + publicAPI = { + require: require, + define: define + }; + + extend(global, publicAPI); + + main(); + +}(this)); diff --git a/js/core/core/application.js b/js/core/core/application.js new file mode 100644 index 0000000..a912b7f --- /dev/null +++ b/js/core/core/application.js @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*global define, console*/ +/*jslint regexp: true*/ + +/** + * Application module + * @requires {@link core/event} + * @requires {@link core/tizen} + * @namespace core/application + * @memberof core + */ + +define({ + name: 'core/application', + requires: [ + 'core/event', + 'core/window', + 'core/tizen' + ], + def: function coreApplication(e, window, tizen) { + 'use strict'; + + var app = null, + navigator = window.navigator, + APP_CONTROL_URL = 'http://tizen.org/appcontrol/'; + + /** + * Gets current application. + * @memberof core/application + */ + function getCurrentApplication() { + return app.getCurrentApplication(); + } + + /** + * Gets application control URI. + * @memberof core/application + * @param {object} operation Operation name. + */ + function getAppControlUri(operation) { + return APP_CONTROL_URL + operation; + } + + /** + * Gets current application id. + * @memberof core/application + */ + function getId() { + return getCurrentApplication().appInfo.id; + } + + /** + * Launches application control. + * @memberof core/application + * @param {object} controlData Control data params. + * @param {string} controlData.operation Operation uri. + * @param {string} [controlData.mime] MIME type. + */ + function launchAppControl(controlData) { + var control = new tizen.ApplicationControl( + getAppControlUri(controlData.operation), + null, + controlData.mime || null + ), + replyCallback = { + onsuccess: function onsuccess(data) { + e.fire( + 'replySuccess', + { + operation: controlData.operation, + data: data + } + ); + }, + onfailure: function onfailure() { + e.fire( + 'replyFailure', + { + operation: controlData.operation + } + ); + } + }; + + try { + app.launchAppControl( + control, + null, + function successCallback() { + e.fire( + 'launchSuccess', + { + operation: controlData.operation + } + ); + }, + function errorCallback(ev) { + e.fire( + 'launchError', + { + operation: controlData.operation, + data: ev + } + ); + }, + replyCallback + ); + } catch (e) { + console.error(e.message); + } + } + + /** + * Returns requeste application control data. + * @memberof core/application + * @param {string} operation Action to be performed. + * @return {object} + */ + function getRequestedAppControlData(operation) { + var rAppControl = getCurrentApplication().getRequestedAppControl(), + appControl = null; + + if (rAppControl) { + appControl = rAppControl.appControl; + + if (appControl.operation === operation) { + return appControl; + } + } + + return null; + } + + /** + * Creates ApplicationControl object. + * @memberof core/application + * @param {string} operation Action to be performed. + */ + function createApplicationControl(operation) { + return new tizen.ApplicationControl(getAppControlUri(operation)); + } + + /** + * Checks if application is running in emulator. + * @return {bool} Is emulated. + */ + function isEmulated() { + return navigator.platform.indexOf('emulated') !== -1; + } + + /** + * Application exit. + * @memberof core/application + */ + function exit() { + getCurrentApplication().exit(); + } + + /** + * Application hide. + * @memberof core/application + */ + function hide() { + getCurrentApplication().hide(); + } + + /** + * No operation. + */ + function noop() { + return; + } + + if (typeof tizen === 'object' && + typeof tizen.application === 'object') { + app = tizen.application; + } else { + console.warn( + 'tizen.application not available, using a mock instead' + ); + app = { + launchAppControl: noop, + getCurrentApplication: function getApp() { + return { + getRequestedAppControl: noop, + exit: noop, + hide: noop + }; + } + }; + } + + return { + getId: getId, + getCurrentApplication: getCurrentApplication, + getAppControlUri: getAppControlUri, + getRequestedAppControlData: getRequestedAppControlData, + launchAppControl: launchAppControl, + createApplicationControl: createApplicationControl, + isEmulated: isEmulated, + hide: hide, + exit: exit + }; + } + +}); diff --git a/js/core/core/event.js b/js/core/core/event.js new file mode 100644 index 0000000..bcf8162 --- /dev/null +++ b/js/core/core/event.js @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*jslint forin: true*/ +/*global define*/ + +/** + * Event module. + * @requires {@link core/window} + * @namespace core/event + * @memberof core + */ + +define({ + name: 'core/event', + requires: [ + 'core/window' + ], + def: function event(window) { + 'use strict'; + + var listeners = {}; + + /** + * Gets listeners name for event. + * @param {string} eventName Event name. + * @return {array} Listeners names. + */ + function getListenersNames(eventName) { + var key, + names = [], + handlers = listeners[eventName]; + + for (key in handlers) { + names.push(handlers[key].name); + } + return names; + } + + /** + * Gets listeners for event. + * @memberof core/event + * @param {string} [eventName] Event name. + * @return {object} Listeners names. + */ + function getListeners(eventName) { + var evName, + names = {}; + + if (eventName) { + names[eventName] = getListenersNames(eventName); + } else { + for (evName in listeners) { + names[evName] = getListenersNames(evName); + } + } + return names; + } + + /** + * Dispatches an event + * @param {string} eventName Event name. + * @param {*} data Detailed data. + * @return {boolean} Cancelled. + */ + function dispatch(eventName, data) { + var customEvent = new window.CustomEvent(eventName, { + detail: data, + cancelable: true + }); + return window.dispatchEvent(customEvent); + } + + /** + * Dispatches an event of given name and detailed data. + * + * Dispatched event name is prefixed with name of + * module which dispatched event. + * + * For example if event with `test` name will be dispatched from + * `foo` module, listener should be added to `foo.test`. + * + * The return value is false if at least one of the event handlers + * which handled this event called event.preventDefault(). + * Otherwise it returns true. + * + * @memberof core/event + * + * @example + * // Define `bar` module which fires an event: + * define({ + * name: 'bar', + * requires: 'core/event', + * def: function def(ev) { + * // Dispatch event + * ev.dispatchEvent('test'); + * + * // or: + * // ev.fire('test'); + * } + * }); + * @see {@link core/event.addEventListener} How to add event listener. + * + * @param {string} eventName Event name. + * @param {*} [data] Detailed data. + * @return {boolean} + */ + function dispatchEvent(eventName, data) { + /*jshint validthis: true */ + var customEvName = this.evName + '.' + eventName; + return dispatch(customEvName, data); + + } + + /** + * Dispatches an event of given name and detailed data. + * + * Since every new fired event has prefixed module name + * this method is deprecated. + * + * For example if event with `test` name will be dispatched from + * `foo` module, listener should be added to `test`. + * + * Please use new dispatchEvent. + * This method can be used only for backward compability. + * + * @memberof core/event + * @deprecated Since v2.0. + * + * @example + * // Define `bar` module which fires an event: + * define({ + * name: 'bar', + * requires: 'core/event', + * def: function def(ev) { + * // Dispatch event + * ev.shoot('test'); + * } + * }); + * @see {@link core/event.addEventListener} How to handle fired event. + * + * @param {string} eventName Event name. + * @param {*} [data] Detailed data. + * @return {boolean} Cancelled. + */ + function shoot(eventName, data) { + return dispatch(eventName, data); + } + + /** + * Adds event listener for event name. + * @param {string} eventName Event name. + * @param {function} handler Handler function. + */ + function addOneEventListener(eventName, handler) { + listeners[eventName] = listeners[eventName] || []; + listeners[eventName].push(handler); + window.addEventListener(eventName, handler); + } + + /** + * Adds event listeners. + * @param {object} listeners Listeners object. + */ + function addEventListeners(listeners) { + var eventName; + for (eventName in listeners) { + if (listeners.hasOwnProperty(eventName)) { + addOneEventListener(eventName, listeners[eventName]); + } + } + } + + /** + * Adds event listener for event name. + * @memberof core/event + * + * @example + * // Define `foo` module which handles event dispatched from `bar` + * // with dispatchEvent method. + * define({ + * name: 'foo', + * requires: 'core/event', + * def: function def(ev) { + * // Add event listener + * ev.addEventListener('bar.test', function handler() {}); + * + * // or: + * // ev.on('bar.test', function handler() {}); + * // ev.listen('bar.test', function handler() {}); + * // ev.listeners('bar.test', function handler() {}); + * + * // Add event listeners using object: + * // ev.on({'bar.test': function handler() {}}); + * // ev.listen({'bar.test': function handler() {}}); + * // ev.listeners({'bar.test': function handler() {}}); + * // ev.addEventListener({ + * // 'bar.test': function handler() {}, + * // }); + * } + * }); + * + * @example + * // Define `foo` module which handles event dispatched from `bar` + * // with shoot method. + * define({ + * name: 'foo', + * requires: 'core/event', + * def: function def(ev) { + * // Add event listener + * ev.addEventListener('test', function handler() {}); + * + * // Or use available aliases for addEventListener + * }); + * @see {@link core/event.dispatchEvent} How to fire event. + * + * @param {string|object} context Event name or Listeners object. + * @param {function} [handler] Handler function. + */ + function addEventListener(context, handler) { + var contextType = typeof context; + if (contextType === 'object') { + addEventListeners(context); + } else if (contextType === 'string') { + addOneEventListener(context, handler); + } + } + + /** + * Removes event listener. + * @memberof core/event + * + * @param {string} eventName Event name. + * @param {function} [handler] Handler function. + */ + function removeEventListener(eventName, handler) { + var i, handlerIndex, listenersLen; + if (handler !== undefined) { + // remove only this specific handler + window.removeEventListener(eventName, handler); + + // find it in the array and clear the reference + handlerIndex = listeners[eventName].indexOf(handler); + if (handlerIndex !== -1) { + listeners[eventName].splice(handlerIndex, 1); + } + } else { + // removes all listeners we know of + listenersLen = listeners[eventName].length; + for (i = 0; i < listenersLen; i += 1) { + window.removeEventListener( + eventName, + listeners[eventName][i] + ); + } + // clear the references + listeners[eventName] = []; + } + } + + return { + + shoot: shoot, // used only for backward compability + + addEventListener: addEventListener, + + /** + * Alias for {@link core/event.addEventListener} + * @memberof core/event + * @function + * @see {@link core/event.addEventListener} + */ + listen: addEventListener, + /** + * Alias for {@link core/event.addEventListener} + * @memberof core/event + * @function + * @see {@link core/event.addEventListener} + */ + listeners: addEventListener, + /** + * Alias for {@link core/event.addEventListener} + * @memberof core/event + * @function + * @see {@link core/event.addEventListener} + */ + on: addEventListener, + + /** + * Alias for {@link core/event.dispatchEvent} + * @memberof core/event + * @function + * @see {@link core/event.dispatchEvent} + */ + fire: dispatchEvent, + dispatchEvent: dispatchEvent, + + /** + * Alias for {@link core/event.removeEventListener} + * @memberof core/event + * @function + * @see {@link core/event.removeEventListener} + */ + die: removeEventListener, + removeEventListener: removeEventListener, + + getListeners: getListeners + }; + } +}); diff --git a/js/core/core/storage/idb.js b/js/core/core/storage/idb.js new file mode 100644 index 0000000..b6b3bf1 --- /dev/null +++ b/js/core/core/storage/idb.js @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*global define, console*/ + +/** + * Simple storage module, implemented using IndexedDB. + * @requires {@link core/event} + * @requires {@link core/window} + * @namespace core/storage/idb + * @memberof core + */ + +define({ + name: 'core/storage/idb', + requires: [ + 'core/event', + 'core/window' + ], + def: function coreStorage(req) { + 'use strict'; + + var global = req.core.window, + indexedDB = global.indexedDB || global.webkitIndexedDB, + IDBKeyRange = global.IDBKeyRange || global.webkitIDBKeyRange, + e = req.core.event, + DB_NAME = 'corestorage', + STORE_NAME = 'kvstore', + VERSION = 5, // increment the value when db structure changes + + // this module fires the following events + EVENT_OPEN = 'open', // db is open and ready to be used + EVENT_READ = 'read', // read request completed + EVENT_WRITE = 'write', // write request completed + EVENT_REMOVE = 'remove', // remove request completed + EVENT_COMPLETED = 'completed', // cleared pending requests + + requestsCounter = -1, + pending = {}, + db = null; + + /** + * @memberof core/storage/idb + */ + function isReady() { + return db !== null; + } + + /** + * Add a request to the list of pending requests. + * @param {string} eventName + * @param {string} key + * @param {*} [value] + * @return {number} unique request ID. + */ + function addPendingRequest(eventName, key, value) { + requestsCounter += 1; + pending[requestsCounter] = { + eventName: eventName, + key: key, + value: value + }; + return requestsCounter; + } + + /** + * Get pending request data. + * Returns 'undefined' if the request is already completed. + * @memberof core/storage/idb + * @param {number} id + * @return {object} + */ + function getPendingRequest(id) { + return pending[id]; + } + + /** + * Checks if there are any pending requests. + * @memberof core/storage/idb + * @return {boolean} + */ + function hasPendingRequests() { + return Object.keys(pending).length !== 0; + } + + /** + * Remove a completed request from the list of pending requests. + * The request should be removed not earlier than all relevant + * handlers have finished processing. + * @param {type} id + */ + function removePendingRequest(id) { + delete pending[id]; + if (!hasPendingRequests()) { + e.fire(EVENT_COMPLETED); + } + } + + /** + * Generic error handler for IndexedDB-related objects. + * @param {Error} err + */ + function onerror(err) { + console.error(err.target.error.message); + } + + /** + * Creates or updates database structure. + * @param {Event} ev + */ + function onUpgradeNeeded(ev) { + var resultDb = ev.target.result; + + // a transaction for changing db version starts automatically + ev.target.transaction.onerror = onerror; + + // remove the existing store and create it again + if (resultDb.objectStoreNames.contains(STORE_NAME)) { + resultDb.deleteObjectStore(STORE_NAME); + } + + resultDb.createObjectStore( + STORE_NAME, + {keyPath: 'key'} + ); + } + + /** + * Assigns database object. + * This handler is executed right after the upgrade. + * This method fires the core.storage.open event upon completion. + * @param {Event} ev + */ + function onOpenSuccess(ev) { + db = ev.target.result; + e.fire(EVENT_OPEN); + } + + /** + * Open the database. + */ + function open() { + // create a request for opening the database + var request = indexedDB.open(DB_NAME, VERSION); + + // one or more of the handlers will be called + // automatically when the current function exits + request.onupgradeneeded = onUpgradeNeeded; + request.onsuccess = onOpenSuccess; + request.onerror = onerror; + } + + /** + * Gets value for given key from the storage. + * The method fires the core.storage.read event upon completion. + * @memberof core/storage/idb + * @param {string} key Key. + * @return {number} Request id. + */ + function get(key) { + var trans = db.transaction([STORE_NAME], 'readwrite'), + store = trans.objectStore(STORE_NAME), + + // Find the key in the store + keyRange = IDBKeyRange.only(key), + id = addPendingRequest(EVENT_READ, key), + cursorRequest = store.openCursor(keyRange); + + cursorRequest.onsuccess = function onCursorOpenSuccess(ev) { + var error = null, + cursor = ev.target.result; + + if (!cursor) { + error = new Error('No records returned'); + error.name = 'StorageNotFoundError'; + + e.fire(EVENT_READ, { + id: id, + key: key, + error: error + }); + removePendingRequest(id); + } else { + e.fire(EVENT_READ, { + id: id, + key: cursor.value.key, + value: cursor.value.value + }); + } + removePendingRequest(id); + }; + + cursorRequest.onerror = function onCursorOpenError(err) { + removePendingRequest(id); + console.error(err.target.error.message); + }; + + return id; + } + + /** + * Sets value for given key to the storage. + * The method fires the core.storage.write event upon completion. + * @memberof core/storage/idb + * @param {string} key Key. + * @param {object} val Value object. + * @return {boolean} + */ + function set(key, val) { + var trans = db.transaction([STORE_NAME], 'readwrite'), + store = trans.objectStore(STORE_NAME), + request = store.put({ + key: key, + value: val + }), + id = addPendingRequest(EVENT_WRITE, key, val); + + request.onsuccess = function onPutSuccess() { + e.fire( + EVENT_WRITE, + {id: id, key: key, value: val} + ); + removePendingRequest(id); + }; + + request.onerror = function onPutError(err) { + console.error(err.target.error.message); + removePendingRequest(id); + }; + + return id; + } + + /** + * Removes value with given key from the storage. + * The method fires the core.storage.remove event upon completion. + * @param {string} key Key name. + * @return {number} id Id. + */ + function removeItem(key) { + var trans = db.transaction([STORE_NAME], 'readwrite'), + store = trans.objectStore(STORE_NAME), + id = addPendingRequest(EVENT_REMOVE, key), + request = store.delete(key); + + request.onsuccess = function onDeleteSuccess() { + e.fire(EVENT_REMOVE, {id: id, key: key}); + removePendingRequest(id); + }; + + request.onerror = function onDeleteError(err) { + console.error(err.target.error.message); + removePendingRequest(id); + }; + return id; + } + + /** + * Removes keys from given array. + * @param {string[]} keys Key array. + */ + function removeItems(keys) { + var ids = []; + keys.forEach(function forEach(key) { + ids.push(removeItem(key)); + }); + return ids; + } + + /** + * Removes value for given context. + * @memberof core/storage/idb + * @param {string|array} context Key name or keys array. + * @return {number|array} id Id or ids array. + */ + function remove(context) { + var id = -1; + if (typeof context === 'string') { + id = removeItem(context); + } else if (Array.isArray(context)) { + id = removeItems(context); + } + return id; + } + + function init() { + open(); + } + + return { + init: init, + getPendingRequest: getPendingRequest, + hasPendingRequests: hasPendingRequests, + isReady: isReady, + get: get, + /** + * Alias for {@link core/storage/idb.set} + * @memberof core/storage/idb + * @function + * @see {@link core/storage/idb.set} + */ + add: set, + set: set, + remove: remove + }; + } +}); diff --git a/js/core/core/systeminfo.js b/js/core/core/systeminfo.js new file mode 100644 index 0000000..21774e9 --- /dev/null +++ b/js/core/core/systeminfo.js @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*global define, console*/ + +/** + * System info module + * @requires {@link core/event} + * @requires {@link core/tizen} + * @namespace core/systeminfo + * @memberof core + */ + +define({ + name: 'core/systeminfo', + requires: [ + 'core/event', + 'core/tizen' + ], + def: function coreSystemInfo(e, tizen) { + 'use strict'; + + var systeminfo = null, + lowBattery = 0.04; + + function noop() { + return; + } + + /** + * Gets system property + * @memberof core/systeminfo + * @param {string} property Property name. + * @param {function} onSuccess Success callback. + * @param {function} onError Error callback. + */ + function getSystemProperty(property, onSuccess, onError) { + systeminfo.getPropertyValue(property, onSuccess, onError); + } + + /** + * Add listener for battery change to low + * @memberof core/systeminfo + * @fires "battery.low" + */ + function listenBatteryLowState() { + systeminfo.addPropertyValueChangeListener( + 'BATTERY', + function change(battery) { + if (!battery.isCharging) { + e.fire('battery.low'); + } + }, + { + lowThreshold: lowBattery + } + ); + } + + /** + * Check low battery state + * @memberof core/systeminfo + * @fires "battery.low" + * @fires "battery.normal" + * @fires "battery.checked" + */ + function checkBatteryLowState() { + systeminfo.getPropertyValue('BATTERY', function getValue(battery) { + if (battery.level < lowBattery && !battery.isCharging) { + e.fire('battery.low', { + level: battery.level + }); + } else { + e.fire('battery.normal'); + } + e.fire('battery.checked'); + }, null); + } + + /** + * Initialise module. + * @memberof core/systeminfo + * @private + */ + function init() { + if (typeof tizen === 'object' && + typeof tizen.systeminfo === 'object') { + systeminfo = tizen.systeminfo; + } else { + console.warn( + 'tizen.systeminfo not available' + ); + systeminfo = { + getPropertyValue: noop, + addPropertyValueChangeListener: noop + }; + } + } + + return { + getSystemProperty: getSystemProperty, + checkBatteryLowState: checkBatteryLowState, + listenBatteryLowState: listenBatteryLowState, + init: init + }; + } +}); diff --git a/js/core/core/tizen.js b/js/core/core/tizen.js new file mode 100644 index 0000000..771cc36 --- /dev/null +++ b/js/core/core/tizen.js @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*global define*/ + +/** + * Tizen module. + * Module returns tizen global object. + * @namespace core/tizen + * @memberof core + * + * @example + * //Define `foo` module which require `core/tizen` module: + * define({ + * name: 'foo', + * requires: ['core/tizen'], + * def: function (tizen) { + * var systeminfo = tizen.systeminfo; + * } + * }); + */ + +define({ + name: 'core/tizen', + requires: ['core/window'], + def: function coreTizen(win) { + 'use strict'; + + return win.tizen; + } +}); diff --git a/js/core/core/window.js b/js/core/core/window.js new file mode 100644 index 0000000..c7f79cc --- /dev/null +++ b/js/core/core/window.js @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*global define, window*/ + +/** + * Window module. + * Module returns window global object. + * @namespace core/window + * @memberof core + * + * @example + * //Define `foo` module which require `core/window` module: + * define({ + * name: 'foo', + * requires: ['core/window'], + * def: function (window) { +* var document = window.document; + * } + * }); + */ + +define({ + name: 'core/window', + def: function coreWindow() { + 'use strict'; + + return window; + } +}); diff --git a/js/helpers/dom.js b/js/helpers/dom.js new file mode 100644 index 0000000..edd1e48 --- /dev/null +++ b/js/helpers/dom.js @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*global define, tizen*/ + +/** + * Dom helper module. + * @namespace Altimeter/helpers/dom + * @memberof Altimeter/helpers + */ + +define({ + name: 'helpers/dom', + def: function helpersDom() { + 'use strict'; + + /** + * Returns parent node with class name given as second parameter + * of the child given as first parameter. + * @memberof Altimeter/helpers/dom + * @param {DOMElement} element + * @param {string} parentClassName + * @return {DOMElement} + */ + function findParentByClassName(element, parentClassName) { + parentClassName = parentClassName.toLowerCase(); + do { + element = element.parentNode; + if (element.classList && + element.classList.contains(parentClassName)) { + return element; + } + } while (element.parentNode); + } + + return { + findParentByClassName: findParentByClassName + }; + } +}); diff --git a/js/models/pressure.js b/js/models/pressure.js new file mode 100644 index 0000000..a6f0f2c --- /dev/null +++ b/js/models/pressure.js @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*global define, console, tizen */ + +/** + * Sensor model. + * @requires {@link core/event} + * @requires {@link core/window} + * @namespace Altimeter/models/pressure + * @memberof Altimeter/models + */ +define({ + name: 'models/pressure', + requires: [ + 'core/event', + 'core/window' + ], + def: function modelsPressure(e, window) { + 'use strict'; + + /** + * Reference to the sensor service. + * @type {SensorService} + */ + var sensorService = null, + + /** + * Reference to the pressure sensor. + * @type {PressureSensor} + */ + pressureSensor = null, + + /** + * Name of the sensor type. + * @type {string} + */ + SENSOR_TYPE = 'PRESSURE', + + /** + * Error type name. + * @type {string} + */ + ERROR_TYPE_NOT_SUPPORTED = 'NotSupportedError', + + /** + * Array of registered pressures. + * @type {number[]} + */ + previousPressures = [], + + /** + * Maximum size of the previousPressures array. + * @type {number} + */ + MAX_LENGTH = 7, + + /** + * Average pressure. + * @type {number} + */ + averagePressure = 0, + + /** + * Current pressure. + * @type {number} + */ + currentPressure = 0; + + /** + * Performs action on start sensor success. + */ + function onSensorStartSuccess() { + e.fire('start'); + } + + /** + * Performs action on start sensor error. + * @param {Error} e + */ + function onSensorStartError(e) { + console.error('Pressure sensor start error: ', e); + e.fire('error', e); + } + + /** + * Updates the average pressure value. + * @param {number} currentPressure + * @return {number} + */ + function updateAveragePressure(currentPressure) { + previousPressures.push(currentPressure); + + var len = previousPressures.length; + + if (len <= MAX_LENGTH) { + // nothing to shift yet, recalculate whole average + averagePressure = previousPressures.reduce(function sum(a, b) { + return a + b; + }) / len; + } else { + // add the new item and subtract the one shifted out + averagePressure += ( + currentPressure - previousPressures.shift() + ) / len; + } + return averagePressure; + } + + /** + * Performs action on sensor change. + * @param {object} data + */ + function onSensorChange(data) { + currentPressure = data.pressure; + updateAveragePressure(currentPressure); + e.fire('change', { + current: data.pressure, + average: averagePressure + }); + } + + /** + * Starts sensor. + * @memberof Altimeter/models/pressure + */ + function start() { + pressureSensor.start(onSensorStartSuccess, onSensorStartError); + } + + /** + * Sets sensor change listener. + * @memberof Altimeter/models/pressure + */ + function setChangeListener() { + pressureSensor.setChangeListener(onSensorChange); + } + + /** + * Returns sensor value. + * @memberof Altimeter/models/pressure + */ + function getSensorValue() { + return currentPressure; + } + + /** + * Returns average of several past readings. + * @memberof Altimeter/models/pressure + * @return {number} + */ + function getAverageSensorValue() { + return averagePressure; + } + + /** + * Handles sensor data. + * @param {object} data + */ + function setCurrentPressureValue(data) { + currentPressure = data.pressure; + } + + /** + * Returns true if sensor is available, false otherwise. + * @memberof Altimeter/models/pressure + * @return {boolean} + */ + function isAvailable() { + return !!pressureSensor; + } + + /** + * Initializes module. + * @memberof Altimeter/models/pressure + */ + function init() { + sensorService = tizen.sensorservice || + (window.webapis && window.webapis.sensorservice) || + null; + + if (!sensorService) { + e.fire('error', {type: 'notavailable'}); + } else { + try { + pressureSensor = sensorService + .getDefaultSensor(SENSOR_TYPE); + pressureSensor + .getPressureSensorData(setCurrentPressureValue); + } catch (error) { + if (error.type === ERROR_TYPE_NOT_SUPPORTED) { + e.fire('error', {type: 'notsupported'}); + } else { + e.fire('error', {type: 'unknown'}); + } + } + } + } + + return { + initSensor: init, + start: start, + isAvailable: isAvailable, + setChangeListener: setChangeListener, + getAverageSensorValue: getAverageSensorValue, + getSensorValue: getSensorValue + }; + } + +}); diff --git a/js/models/settings.js b/js/models/settings.js new file mode 100644 index 0000000..a967641 --- /dev/null +++ b/js/models/settings.js @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*global define, console*/ + +/** + * Settings model. + * @requires {@link core/storage/idb} + * @requires {@link core/event} + * @namespace Altimeter/models/settings + * @memberof Altimeter/models + */ +define({ + name: 'models/settings', + requires: [ + 'core/storage/idb', + 'core/event' + ], + def: function modelsSettings(req) { + 'use strict'; + + /** + * Core event module. + * @type {Module} + */ + var e = req.core.event, + + /** + * Core IDB storage module. + * @type {Module} + */ + s = req.core.storage.idb, + + /** + * Storage key. + * @type {string} + */ + STORAGE_KEY = 'settings', + + /** + * Default pressure. + * @type {object} + */ + DEFAULT = Object.freeze({ + pressure: 1013.25 + }), + + /** + * Settings object. + * @type {object} + */ + settings = {}; + + /** + * Saves settings to storage. + */ + function saveSettings() { + s.add(STORAGE_KEY, settings); + } + + /** + * Sets given settings property. + * @memberof Altimeter/models/settings + * @param {string} property + * @param {number} value + * @return {boolean} + */ + function set(property, value) { + if (property !== undefined && value !== undefined) { + settings[property] = value; + saveSettings(); + return true; + } + return false; + } + + /** + * Returns given settings property. + * @memberof Altimeter/models/settings + * @param {string} property + * @return {number} + */ + function get(property) { + if (settings[property] === undefined) { + console.error('Settings not initialized yet.'); + return null; + } + return settings[property]; + } + + /** + * Initializes module. + */ + function init() { + s.get(STORAGE_KEY); + } + + /** + * Handles core.storage.idb.read event. + * @param {event} ev + */ + function onRead(ev) { + if (ev.detail.key !== STORAGE_KEY) { + return; + } + if (typeof ev.detail.value !== 'object') { + settings = { + pressure: DEFAULT.pressure + }; + saveSettings(); + } else { + settings = ev.detail.value; + } + e.fire('ready'); + } + + /** + * Make sure that init is run when storage is ready. + * @memberof Altimeter/models/settings + */ + function runInit() { + if (s.isReady()) { + init(); + } else { + e.listen('core.storage.idb.open', init); + } + } + + e.listeners({ + 'core.storage.idb.read': onRead + }); + + return { + init: runInit, + get: get, + set: set + }; + } + +}); diff --git a/js/tau-config.js b/js/tau-config.js new file mode 100644 index 0000000..0ee1614 --- /dev/null +++ b/js/tau-config.js @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*global document, tau*/ + +document.addEventListener('tauinit', function onTauInit() { + 'use strict'; + + tau.setConfig('pageTransition', 'none'); + tau.setConfig('popupTransition', 'none'); +}); diff --git a/js/views/init.js b/js/views/init.js new file mode 100644 index 0000000..d048b4e --- /dev/null +++ b/js/views/init.js @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*global define, console, window, document*/ + +/** + * Init page module. + * @requires {@link core/event} + * @requires {@link core/application} + * @requires {@link core/systeminfo} + * @requires {@link Altimeter/views/main} + * @namespace Altimeter/views/init + * @memberof Altimeter/views + */ +define({ + name: 'views/init', + requires: [ + 'core/event', + 'core/application', + 'core/systeminfo', + 'views/main' + ], + def: function viewsInitPage(req) { + 'use strict'; + + /** + * Core event module. + * @type {Module} + */ + var e = req.core.event, + + /** + * Core application module. + * @type {Module} + */ + app = req.core.application, + + /** + * Core systeminfo module. + * @type {Module} + */ + sysInfo = req.core.systeminfo; + + /** + * Registers view event listeners. + */ + function bindEvents() { + document.addEventListener('tizenhwkey', function onTizenhwkey(e) { + if (e.keyName === 'back') { + app.exit(); + } + }); + sysInfo.listenBatteryLowState(); + } + + /** + * Handles the core.battery.low event. + */ + function onLowBattery() { + app.exit(); + } + + /** + * Handles the core.battery.checked state. + */ + function onBatteryChecked() { + e.fire('device.ready'); + } + + /** + * Initializes the module. + * @memberof Altimeter/views/init + */ + function init() { + bindEvents(); + sysInfo.checkBatteryLowState(); + } + + e.listeners({ + 'core.systeminfo.battery.low': onLowBattery, + 'core.systeminfo.battery.checked': onBatteryChecked + }); + + return { + init: init + }; + } + +}); diff --git a/js/views/main.js b/js/views/main.js new file mode 100644 index 0000000..8dd19e3 --- /dev/null +++ b/js/views/main.js @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*global define, console, document, tau*/ + +/** + * Main page module. + * @requires {@link core/event} + * @requires {@link Altimeter/models/settings} + * @requires {@link Altimeter/models/pressure} + * @requires {@link core/application} + * @namespace Altimeter/views/main + * @memberof Altimeter/views + */ + +define({ + name: 'views/main', + requires: [ + 'core/event', + 'models/settings', + 'models/pressure', + 'core/application' + ], + def: function viewsMainPage(req) { + 'use strict'; + + /** + * Core event module. + * @type {Module} + */ + var e = req.core.event, + + /** + * Pressure module. + * @type {Module} + */ + sensor = req.models.pressure, + + /** + * Settings module. + * @type {Module} + */ + settings = req.models.settings, + + /** + * Core event module. + * @type {Module} + */ + app = req.core.application, + + /** + * Popup message. + * @type {string} + */ + ARE_YOU_SURE_MSG = 'Are you sure you want to calibrate device?', + + /** + * Popup message. + * @type {string} + */ + SENSOR_NOT_AVAILABLE_MSG = 'Pressure sensor is not available.', + + /** + * Popup message. + * @type {string} + */ + SENSOR_NOT_SUPPORTED_MSG = 'Pressure sensor is not supported ' + + 'on this device.', + + /** + * Popup message. + * @type {string} + */ + SENSOR_UNKNOWN_ERROR_MSG = 'Unknown sensor error occurs.', + + /** + * Reference to the popup message element. + * @type {HTMLElement} + */ + calibrationMessage = null, + + /** + * Reference to the 'Calibration' button. + * @type {HTMLElement} + */ + calibrationBtn = null, + + /** + * Reference to the 'Yes' button. + * @type {HTMLElement} + */ + yesBtn = null, + + /** + * Reference to the 'No' button. + * @type {HTMLElement} + */ + noBtn = null, + + /** + * Reference to the 'reference' element. + * @type {HTMLElement} + */ + referenceValue = null, + + /** + * Reference to the 'pressure' element. + * @type {HTMLElement} + */ + pressureValue = null, + + /** + * Reference to the 'altitude' element. + * @type {HTMLElement} + */ + altitudeValue = null, + + /** + * Reference to the 'alert' popup. + * @type {HTMLElement} + */ + alertElement = null, + + /** + * Reference to the alert message element. + * @type {HTMLElement} + */ + alertMessage = null, + + /** + * Reference to the 'ok' button on the alert popup. + * @type {HTMLElement} + */ + alertOk = null, + + /** + * Reference to the 'calibration' popup. + * @type {HTMLElement} + */ + popupCalibration = null; + + /** + * Updates reference pressure value. + */ + function updateReferenceValue() { + referenceValue.innerText = settings.get('pressure').toFixed(2); + } + + /** + * Updates current pressure value. + * @param {number} value + */ + function updatePressureValue(value) { + pressureValue.innerText = value.toFixed(2); + } + + /** + * Updates altitude value. + * @param {number} value + */ + function updateAltitudeValue(value) { + var reference = settings.get('pressure'), + text = '', + altitude = -8727 * Math.log(value / reference); + + text = altitude.toFixed(0); + if (text === '-0') { + text = '0'; + } + altitudeValue.innerText = text; + } + + /** + * Resets altitude value. + */ + function resetAltitudeValue() { + altitudeValue.innerText = '0'; + } + + /** + * Shows application working space. + */ + function showWorkingSpace() { + updateReferenceValue(); + } + + /** + * Shows application start monit. + */ + function showCalibrationMonit() { + tau.openPopup(popupCalibration); + } + + /** + * Calibrates pressure. + */ + function calibratePressure() { + settings.set('pressure', sensor.getAverageSensorValue()); + resetAltitudeValue(); + updateReferenceValue(); + } + + /** + * Shows alert popup. + * @param {string} message Message. + */ + function openAlert(message) { + alertMessage.innerHTML = message; + tau.openPopup(alertElement); + } + + /** + * Handles click event on calibration button. + */ + function onCalibrationBtnClick() { + calibrationMessage.innerHTML = ARE_YOU_SURE_MSG; + showCalibrationMonit(); + } + + /** + * Handles click event on yes button. + */ + function onYesBtnClick() { + tau.closePopup(); + showWorkingSpace(); + calibratePressure(); + } + + /** + * Handles click event on no button. + */ + function onNoBtnClick() { + tau.closePopup(); + showWorkingSpace(); + } + + /** + * Handles sensor.start event. + */ + function onSensorStart() { + showCalibrationMonit(); + } + + /** + * Handles models.pressure.error event. + * @param {object} data + */ + function onSensorError(data) { + var type = data.detail.type; + + if (type === 'notavailable') { + openAlert(SENSOR_NOT_AVAILABLE_MSG); + } else if (type === 'notsupported') { + openAlert(SENSOR_NOT_SUPPORTED_MSG); + } else { + openAlert(SENSOR_UNKNOWN_ERROR_MSG); + } + } + + /** + * Handles sensor.change event. + * @param {Event} ev + */ + function onSensorChange(ev) { + updatePressureValue(ev.detail.average); + updateAltitudeValue(ev.detail.average); + } + + /** + * Handles device.ready event. + */ + function onDeviceReady() { + sensor.initSensor(); + if (sensor.isAvailable()) { + sensor.setChangeListener(); + sensor.start(); + } + } + + /** + * Handles click event on OK button. + */ + function onOkClick() { + tau.closePopup(); + } + + /** + * Handles popupHide event on popup element. + */ + function onPopupHide() { + app.exit(); + } + + /** + * Registers event listeners. + */ + function bindEvents() { + calibrationBtn.addEventListener('click', onCalibrationBtnClick); + yesBtn.addEventListener('click', onYesBtnClick); + noBtn.addEventListener('click', onNoBtnClick); + alertElement.addEventListener('popuphide', onPopupHide); + alertOk.addEventListener('click', onOkClick); + } + + /** + * Initializes module. + * @memberof Altimeter/views/main + */ + function init() { + calibrationMessage = document.getElementById( + 'popup-calibration-message' + ); + calibrationBtn = document.getElementById('calibration-btn'); + yesBtn = document.getElementById( + 'popup-calibration-yes' + ); + noBtn = document.getElementById( + 'popup-calibration-no' + ); + referenceValue = document.getElementById('reference-value'); + pressureValue = document.getElementById('current-value'); + altitudeValue = document.getElementById('altitude-value'); + alertElement = document.getElementById('alert'); + alertMessage = document.getElementById('alert-message'); + alertOk = document.getElementById('alert-ok'); + popupCalibration = document.getElementById('popup-calibration'); + bindEvents(); + } + + e.listeners({ + 'models.pressure.start': onSensorStart, + 'models.pressure.error': onSensorError, + 'models.pressure.change': onSensorChange, + 'views.init.device.ready': onDeviceReady + }); + + return { + init: init + }; + } + +}); |